{ "info": { "author": "St\u00e9phane \"Twidi\" Angel", "author_email": "s.angel@twidi.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3 :: Only" ], "description": "####\nMIXT\n####\n\nWrite html components directly in python and you have a beautiful but controversial MIXTure.\n\nYes, **controversial**.\n\n**If you don't like it, ignore it** (but you can use this without the html-in-python part, see below ;))\n\n*Based* on `pyxl `_. Python 3.6+ only, and use typing for data validation.\n\nOnce you have your html, you can do whatever you want with it. Think of it as a replacement for your classic template engine.\n\n**Source code**: ``_\n\n**Documentation**: ``_\n\n**PyPI**: ``_\n\n**CI** (CircleCi): ``_\n\n***********\nBasic usage\n***********\n\nLet's create a file ``example.py``\n\n.. code-block:: python\n\n # coding: mixt\n\n from mixt import html, Element, Required\n\n class Hello(Element):\n class PropTypes:\n name: Required[str]\n\n def render(self, context):\n return
Hello, {self.name}
\n\n print()\n\n\nAnd execute it:\n\n.. code-block:: shell\n\n $ python example.py\n
Hello, World
\n\n\nIf you don't like to write html in python, you can still use it:\n\n.. code-block:: python\n\n from mixt import html, Element, Required\n\n class Hello(Element):\n class PropTypes:\n name: Required[str]\n\n def render(self, context):\n return html.Div()(\"Hello, \", self.name)\n\n print(Hello(name=\"World\"))\n\n\n********\nFeatures\n********\n\nYes it is inspired by React (in fact, mainly JSX) and we borrow some of the concept:\n\n- props and PropTypes with validation\n- dev-mode to validate props in dev but not in production to speed things up (your tests should have tested that everything is ok)\n- context\n- class components or simple function components\n- high order components\n\nAnd we added:\n\n- css/js collectors\n\n\n************\nInstallation\n************\n\nRun these two commands. The second one will tell python how to understand files with html inside.\n\n.. code-block:: shell\n\n pip install mixt\n mixt-post-install\n\nTo check that everything is ready, run:\n\n.. code-block:: shell\n\n python -m mixt.examples.simple\n\nYou should have this output:\n\n.. code-block:: html\n\n
Hello, World
\n\nIf you don't want to use the html-in-python stuff, don't run ``mixt-post-install``. And then test with (to have the same output):\n\n.. code-block:: shell\n\n python -m mixt.examples.simple_pure_python\n\n**********\nContribute\n**********\n\nClone the git project then:\n\n.. code-block:: shell\n\n make dev\n\n\nTo check that everything is ready, run:\n\n.. code-block:: shell\n\n python -m mixt.examples.simple\n\n\nYou should have this output:\n\n.. code-block:: html\n\n
Hello, World
\n\n\nAfter having done some code:\n\n.. code-block:: shell\n\n make tests\n\n\n.. code-block:: shell\n\n make lint\n\n\nIf you touch things in the ``codec`` directory, you'll have to run ``make dev`` (or at least ``make full-clean``) to purge the ``pyc`` python files.\n\nNote that our CI will check that every commit passes the ``make lint``, ``make tests`` and ``make check-doc``. So don't forget to run these for each commit.\n\nOne way to do it before pushing is:\n\n.. code-block:: shell\n\n git rebase develop --exec 'git log -n 1; make checks'\n\n\n**********\nUser Guide\n**********\n\nNote: You can find the *final* code of this user guide in ``src/mixt/examples/user_guide`` (you'll find ``mixt.py`` and ``pure_python.py``).\n\nRun it with:\n\n.. code-block:: shell\n\n python -m mixt.examples.user_guide\n\n\nStart\n=====\n\nLet's create a... todo list, yeah!\n\nBut before, remember. This is not React, it's not on the browser and there is no Javascript involved here. We only talk about rendering some HTML.\n\nBut you can do what you want with it. Add javascript handlers, simple forms...\n\nTalking about forms...\n\nIn a todo list, we want to be able to add a todo. It's a simple text input.\n\nSo let's create our first component, the ``TodoForm``. We want a form with an input text and a button.\n\nA component is a subclass of the ``Element`` class, with a ``render`` method you have to write.\n\n.. code-block:: python\n\n # coding: mixt\n\n from mixt import Element, html # html is mandatory to resolve html tags\n\n class TodoForm(Element):\n\n def render(self, context): # Ignore the ``context`` argument for now.\n return \\ # The ``\\`` is only for a better indentation below\n
\n \n \n \n\n\nNote that this could have been written as a simple function:\n\n.. code-block:: python\n\n # coding: mixt\n\n from mixt import Element, html\n\n def TodoForm():\n return \\\n
\n \n \n \n\n\nWhen print the component, these two will give the same result:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n
\n\n\nSpacing\n=======\n\nNotice how it is formatted: no space between tags. In fact, it's `like in JSX `_:\n\n JSX removes whitespace at the beginning and ending of a line. It also removes blank lines. New lines adjacent to tags are removed; new lines that occur in the middle of string literals are condensed into a single space\n\nTo add a space, or a newline, you can pass some python. Let's, as an example, add a newline before the label:\n\n.. code-block:: python\n\n #...\n
\n {'\\n'}\n #...\n\n\nNow we have this output:\n\n.. code-block:: html\n\n \n \n\n\nProps\n=====\n\nNow let's go further.\n\nNotice the ``action`` attribute of the form. We need to pass something. But hard-coding it does not sound right. Wwe need to pass it to the component.\n\n``Mixt`` has, like ``React``, the concept of properties, aka \"props\".\n\n\nPropTypes class\n---------------\n\nIn ``Mixt``, we define them with a type, in a class inside our component, named ``PropTypes``:\n\n.. code-block:: python\n\n class TodoForm(Element):\n\n class PropTypes:\n add_url: str\n\n def render(self, context):\n return \\\n
\n \n \n \n\n\nHere we defined a prop named ``add_url`` which must be a string (``str``). This uses the `python typing syntax `_.\n\nAnd notice how we changed the ``action`` attribute of the ``form`` tag. It's now ``{self.add_url}`` instead of ``\"???\"``.\n\nWhen attributes are passed between curly braces, they are interpreted as pure python at run-time. In fact, as the ``mixt`` parser will convert the whole file to pure python before letting the python interpreter running it, it will stay the same, only the html around will be converted. So there is no penalty to do this.\n\n\nProps and children\n------------------\n\nLook how this would look like if our component was written in pure python:\n\n.. code-block:: python\n\n from mixt import Element, html\n\n class TodoForm(Element):\n\n class PropTypes:\n add_url: str\n\n def render(self, context):\n return html.Form(method='post', action=self.add_url )(\n html.Label()(\n html.Raw(\"New Todo: \")\n ),\n html.InputText(name='todo'),\n html.Button(type='submit')(\n html.Raw(\"Add\") # or html.Rawhtml(text=\"Add\")\n ),\n )\n\n\nNotice how the props are passed to a component as named arguments and how ``action`` is passed: ``action=self.add_url``.\n\nThis pure-python component also shows you how it works: props are passed as named argument to the component class, then this component is called, passing children components as positional arguments to the call:\n\n.. code-block:: python\n\n ComponentClass(prop1=\"val1\", prop2=\"val2\")(\n Children1(),\n Children2(),\n )\n\nWhat are children? Children are tags inside other tags.\n\nIn ``

foo

``, we have:\n\n- a ``html.Div`` component, with a prop ``id`` and two children:\n\n - a ``html.Span`` component, without children\n - a ``html.P`` component with one child:\n\n - a ``html.RawHtml`` component with the text \"foo\"\n\n\nNote that you can play with props and children. First the version in pure-python to show how it works:\n\n.. code-block:: python\n\n def render(self, context):\n props = {\"prop1\": \"val1\", \"prop2\": \"val2\"}\n children = [Children1(), Children2()]\n\n return ComponentClass(**props)(*children)\n # You can pass a list of children to to the call, so this would produce the same result:\n # ComponentClass(**props)(children)\n\n\nThen the ``mixt`` version:\n\n.. code-block:: python\n\n def render(self, context):\n props = {\"prop1\": \"val1\", \"prop2\": \"val2\"}\n children = [, ]\n\n return {*children}\n # or, the same, passing the children as a list:\n # return {children}\n\n\nPassing props\n-------------\n\nNow let's go back to our props ``add_url``.\n\nHow to pass it to the component?\n\nThe exact same way we passed attributes to HTML tags: they are in fact props defined in the HTML compoments (defined in ``mixt.html``). We support every HTML tags that, at the time of the writing, are valid (not deprecated) in HTML5, with their attributes (excluding the deprecated ones).\n\nSo let's do this:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n
\n\nOK, we have our prop present in the rendered HTML.\n\nValidation\n----------\n\nWhat if we don't pass a string? We said in ``PropTypes`` that we wanted a string...\n\nNumbers\n^^^^^^^\n\nLet's try it:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n
\n\n\nIt works! But... it's not a string!! In fact, there is a special case for numbers, you can pass them as numbers instead of strings and they are converted if needed...\n\n\nBooleans and other special cases\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSo let's try something else.\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropValueError:\n .add_url: `True` is not a valid value for this prop (type: , expected: )\n\n\nAnd it's the same if we pass ``True`` in python\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropValueError:\n .add_url: `True` is not a valid value for this prop (type: , expected: )\n\n\nOk, let's trick the system and pass ``\"True\"``, as a string.\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropValueError:\n .add_url: `True` is not a valid value for this prop (type: , expected: )\n\n\nStill the same, but here we passed a string! Yes but there are 4 values that are always evaluated to what they seems to be:\n\n- True\n- False\n- None\n- NotProvided (a special value meaning \"not set\" which is different that ``None``)\n\nThe only way to pass one of these values as a string, is to pass them via python, as a string:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n
\n\n\nExcept these 4 values, and numbers, every value that is passed to an attribute is considered a string. Even if there is no quotes, like in html in HTML5, where quotes are not mandatory for strings without some characters (no spaces, no ``/``...).\n\nTo pass something else, you must surround the value in curly braces (and in this cases there is no need for quotes around the curly braces.\n\n\nOk, now we are sure that we only accept string.... but what if I pass nothing? And... what is \"nothing\"?\n\nLet's start with an empty string in python:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n
\n\n\nOk it works, we wanted a string, we have a string.\n\nNow let's pass this empty string directly:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n
\n\n\nIt still works, because it's still a string. Let's remove the quotes, to see.\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.GeneralParserError: Unclosed Tags: \n\n\nHum yeah, this is not valid HTML. So let's remove the ``=``:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropValueError:\n .add_url: `True` is not a valid value for this prop (type: , expected: )\n\n\nWHAT? Yes, think about HTML5 attributes like ``required``, ``checked``... They only need to be present as an attribute, without value, to be considered ``True``. So when an attribute doesn't have any value, it's a boolean, and it's ``True``.\n\nIn addition to not pass a value, those two other ways are valid in HTML5 for a boolean to by ``True``:\n\n- pass an empty string: ``required=\"\"``\n- pass the name of the attribute: ``required=\"required\"``\n\nFor your convenience, we added another way:\n\n- pass ``True`` (case does not matter), as python or as a string: ``required=True``, ``required={True}``, ``required=\"true\"``\n\nAnd its counterpart, to pass ``False``:\n\n- pass ``False`` (case does not matter), as python or as a string: ``required=False``, ``required={False}``, ``required=\"false\"``\n\n\nRequired props\n--------------\n\nOk for the boolean attributes. It's not our case. The last thing we can do is to not set the attribute at all:\n\n.. code-block:: python\n\n print()\n # this is the same: ``print()```\n # (``NotProvided`` must be imported from ``mixt``)\n\n.. code-block:: python\n\n mixt.exceptions.UnsetPropError: .add_url: prop is not set\n\n\nIt's understandable: we try to access a prop that is not set, of course we cannot use it.\n\n\nBut what if we don't access it? If we don't print the component, it won't be rendered:\n\n.. code-block:: python\n\n \n\n.. code-block:: python\n\n \n\n\nSo we can create an instance but it will fail at render time. But there is a way to prevent that.\n\nBy default, all properties are optional. And you don't have to use the ``Optional`` type from the python ``typing`` module for that, it would be cumbersome to do it for each prop.\n\nInstead, ``mixt`` provides a type named ``Required`` that you use the same way than ``Optionnal``.\n\n.. code-block:: python\n\n from mixt import Element, Required, html\n\n class TodoForm(Element):\n\n class PropTypes:\n add_url: Required[str]\n\n def render(self, context):\n # ...\n\n\nSo we just said we wanted a string, and that it is required.\n\nLet's try again to create it without the prop:\n\n.. code-block:: python\n\n \n\n.. code-block:: python\n\n mixt.exceptions.RequiredPropError: .add_url: is a required prop but is not set\n\n\nNow we have the exception raised earlier in our program.\n\n\nDefault props\n-------------\n\nTo see other possibilities in props, let's add a new one to change the text label. But we don't want to make it required, and instead have a default value.\n\nFor this, it's as easy as adding a value to the prop in the ``PropTypes`` class:\n\n.. code-block:: python\n\n class TodoForm(Element):\n\n class PropTypes:\n add_url: Required[str]\n label: str = 'New Todo'\n\n def render(self, context):\n return \\\n
\n \n \n \n\n\nNow let's try it without passing the prop:\n\n.. code-block:: python\n\n print()\n\n\n.. code-block:: html\n\n
\n\n\nAnd if we pass one:\n\n.. code-block:: python\n\n print()\n\n\n.. code-block:: html\n\n
\n\n\nIt works as expected.\n\nNote that you cannot give a default value while having the prop ``Required``. It makes no sense, so it's checked as soon as possible, while the ``class`` is constructed:\n\n.. code-block:: python\n\n class TodoForm(Element):\n\n class PropTypes:\n add_url: Required[str]\n label: Required[str] = 'New Todo'\n\n.. code-block:: python\n\n mixt.exceptions.PropTypeRequiredError: .label: a prop with a default value cannot be required\n\n\nAnd of course the default value must match the type!\n\n.. code-block:: python\n\n class TodoForm(Element):\n\n class PropTypes:\n add_url: Required[str]\n label: str = {'label': 'foo'}\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropValueError:\n .label: `{'label': 'foo'}` is not a valid value for this prop (type: , expected: )\n\n\nChoices\n-------\n\nAnother thing we want to do in our component is to let it construct the label, passing it a \"type\" of todo, but limiting the choices. For this we can use the ``Choices`` type:\n\n.. code-block:: python\n\n from mixt import Choices, Element, Required, html\n\n\n class TodoForm(Element):\n\n class PropTypes:\n add_url: Required[str]\n type: Choices = ['todo', 'thing']\n\n def render(self, context):\n\n return \\\n
\n \n \n \n\n\nLet's try it:\n\n.. code-block:: python\n\n print()\n print()\n\n.. code-block:: html\n\n
\n
\n\n\nAnd what if we try to pass something else than the available choices? It fails, as expected:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropChoiceError: .type: `stuff` is not a valid choice for this prop (must be in ['todo', 'thing'])\n\n\nDefault choices\n---------------\n\nBut maybe we don't want to pass it and use a default value. What would the result be?\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.UnsetPropError: .type: prop is not set\n\n\nSo we have to mark the ``type`` prop as required:\n\n.. code-block:: python\n\n class PropTypes:\n add_url: Required[str]\n type: Required[Choices] = ['todo', 'thing']\n\n\nSo if we don't pass it, it fails earlier:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.RequiredPropError: .type: is a required prop but is not set\n\n\nBut it's not what we want, we want a default value.\n\nIn fact, you noticed that for types other than ``Choices``, setting a value in ``PropTypes`` gives us a default value. But for ``Choices`` it's different, as the value is the list of choices.\n\nFor this, we have ``DefaultChoices``: it work the same as ``Choices``, but use the first entry in the list as the default value. And of course, as with other types having default, it cannot be ``Required``.\n\nLet's try it:\n\n.. code-block:: python\n\n from mixt import DefaultChoices, Element, Required, html\n\n\n class TodoForm(Element):\n\n class PropTypes:\n add_url: Required[str]\n type: DefaultChoices = ['todo', 'thing']\n\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n
\n\nIt works as expected.\n\n\nAdvanced types\n--------------\n\nUntil then, we used simple types, but you can use more complicated ones.\n\nSo for example, we'll make the ``add_url`` prop to accept a function that will compute the url for us based on the ``type`` prop. But we also want to allow strings, and with a default value.\n\nWe can do that, with typing. Our function will take a string, the ``type`` and will return a string, the url.\n\nSo the `syntax `_ is ``Callable[[str], str]`` for the callable, and we use ``Union`` to accept things of type ``Callable`` or ``str``:\n\n.. code-block:: python\n\n from typing import Union, Callable\n from mixt import DefaultChoices, Element, Required, html\n\n\n class TodoForm(Element):\n\n class PropTypes:\n add_url: Union[Callable[[str], str], str] = \"/todo/add\"\n type: DefaultChoices = ['todo', 'thing']\n\n def render(self, context):\n\n if callable(self.add_url):\n add_url = self.add_url(self.type)\n else:\n add_url = self.add_url\n\n return \\\n
\n \n \n \n\nFirst, let's try it without the ``add_url`` prop, as we have a default:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n
\n\n\nIt should work too if we pass a string:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n
\n\n\nAnd now we can pass a function:\n\n.. code-block:: python\n\n def make_url(type):\n return f\"/{type}/add\"\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropValueError: .add_url:\n `` is not a valid value for this prop (type: , expected: Union[Callable[[str], str], str])\n\n\nOh? Why? I passed a function accepting a string as argument and returning a string. Yes, but don't forget that types are checked! So we must add types to our function:\n\n.. code-block:: python\n\n def make_url(type: str) -> str:\n return f\"/{type}/add\"\n\n print()\n\n.. code-block:: html\n\n
\n\n\nAnd if we pass another type, the url should change accordingly:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n
\n\n\nWe can even make this function the default value for our prop:\n\n.. code-block:: python\n\n from typing import Union, Callable\n from mixt import DefaultChoices, Element, Required, html\n\n\n def make_url(type: str) -> str:\n return f\"/{type}/add\"\n\n\n class TodoForm(Element):\n\n class PropTypes:\n add_url: Union[Callable[[str], str], str] = make_url\n type: DefaultChoices = ['todo', 'thing']\n\n def render(self, context):\n\n if callable(self.add_url):\n add_url = self.add_url(self.type)\n else:\n add_url = self.add_url\n\n return \\\n
\n \n \n \n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n
\n\n\ndev-mode\n========\n\nNow you may start wondering... python typing is cumbersome and validating may take away some of our precious time.\n\nLet's me answer that:\n\n1. No, typing is not cumbersome. It's really useful to spot bugs and add some self-documentation.\n2. Yes, it takes away some of our precious time. But we got you covered.\n\nBy default, ``mixt`` run in \"dev-mode\". And in dev-mode, props are validated when passed to a component. When you are NOT in \"dev-mode\", the validation is skipped. So in production, you can deactivate the dev-mode (we'll see how in a minute) and pass props very fast:\n\n- we don't check required props (but that would fail if you try to use it in your compoment)\n- we don't check if a ``Choices`` prop is, indeed, in the list of choices\n- we don't check the type at all, so for example if you want to pass a list for a string, it will work but with understandable strange things happening in your ``render`` method.\n\nBut you may say that it's in production that validation is important. Indeed. But of course your code is fully covered by tests, that you run in dev-mode, and so in production, you don't need this validation! And note that it's how React works, by the way, with ``NODE_ENV=production``.\n\nHow to change dev-mode? We don't enforce any environment variable but we propose some functions. It's up to you to call them:\n\n.. code-block:: python\n\n from mixt import set_dev_mode, unset_dev_mode, override_dev_mode, in_dev_mode\n\n # by default, dev-mode is active\n assert in_dev_mode()\n\n # you can unset the dev-mode\n unset_dev_mode()\n assert not in_dev_mode()\n\n # and set it back\n set_dev_mode()\n assert in_dev_mode()\n\n # set_dev_mode can take a boolean\n set_dev_mode(False)\n assert not in_dev_mode()\n\n set_dev_mode(True)\n assert in_dev_mode()\n\n # and we have a context manager to override for a block\n with override_dev_mode(False):\n assert not in_dev_mode()\n with override_dev_mode(True):\n assert in_dev_mode()\n assert not in_dev_mode()\n assert in_dev_mode()\n\n\nSo let's try this with the ``type`` prop. Remember, it looks like:\n\n.. code-block:: python\n\n type: DefaultChoices = ['todo', 'thing']\n\nWe try to pass another choice, first in dev-mode:\n\n.. code-block:: python\n\n with override_dev_mode(True):\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropChoiceError: .type: `stuff` is not a valid choice for this prop (must be in ['todo', 'thing'])\n\nIt fails as expected.\n\nAnd now by deactivating dev-mode:\n\n.. code-block:: python\n\n with override_dev_mode(False):\n print()\n\n.. code-block:: html\n\n
\n\nIt works, we have a todo type that was not in our choices that is used, and is in the ``action`` too. It's the work of your tests to ensure that you never pass invalid props, so you can be confident in production and deactivate dev-mode.\n\n\nComponents cascade\n==================\n\nNow we have our form. What other components do we need for our todo list app?\n\nOf course, we need a way to display a todo entry.\n\nBut what is a todo entry? Let's create a basic ``TodoObject``:\n\n.. code-block:: python\n\n class TodoObject:\n def __init__(self, text):\n self.text = text\n\n\nIt's a very simple class, but you can use what you want, of course. It could be Django models, etc...\n\nSo we can create our ``Todo`` component, making it accept a required ``TodoObject`` as prop:\n\n.. code-block:: python\n\n class Todo(Element):\n class PropTypes:\n todo: Required[TodoObject]\n\n def render(self, context):\n return
  • {self.todo.text}
  • \n\nAnd we can use it:\n\n.. code-block:: python\n\n todo = TodoObject(\"foo\")\n print()\n\n.. code-block:: html\n\n
  • foo
  • \n\n\nNow we want to have a list of todos. Let's create a ``TodoList`` component that will accept as props a list of ``TodoObject``.\n\nBut what is different than our two other components, that only use html tags in their ``render`` method, it's that now we will encapsulate a component into another. Let's see how.\n\n.. code-block:: python\n\n class TodoList(Element):\n\n class PropTypes:\n todos: Required[List[TodoObject]]\n\n def render(self, context):\n return
      {[ for todo in self.todos]}
    \n\n\nYes, it's as simple as that: you use ```` for the ``Todo`` component as you would use an HTML tag. The only difference is that for html tags, you don't need to import them directly (simple import ``html`` from ``mixt``), and by convention we write them in lower-case. For normal components, you have to import them (you can still do ``from mylib import components`` and ````) and use the exact case.\n\nNotice how we required a list, and passed it into the ``
      `` via a list-comprehension in curly-braces.\n\nYou can do things differently if you want.\n\nLike separating the list comprehension from the html:\n\n.. code-block:: python\n\n def render(self, context):\n todos = [\n \n for todo\n in self.todos\n ]\n return
        {todos}
      \n\nOr in a dedicated method (that would be useful for testing):\n\n.. code-block:: python\n\n def render_todos(self, todos):\n return [\n \n for todo\n in todos\n ]\n\n def render(self, context):\n return
        {self.render_todos(self.todos)}
      \n\n\nIt's up to you: at the end it's just python.\n\nLet's see what is rendered by this component:\n\n.. code-block:: python\n\n todos = [TodoObject(\"foo\"), TodoObject(\"bar\"), TodoObject(\"baz\")]\n print()\n\n.. code-block:: html\n\n
      • foo
      • bar
      • baz
      \n\n\nAnd finally we have our ``TodoApp`` component that encapsulate the form and the list:\n\n.. code-block:: python\n\n class TodoApp(Element):\n\n class PropTypes:\n todos: Required[List[TodoObject]]\n type: DefaultChoices = ['todo', 'thing']\n\n def render(self, context):\n return \\\n
      \n

      The \"{self.type}\" list

      \n \n \n
      \n\n.. code-block:: python\n\n todos = [TodoObject(\"foo\"), TodoObject(\"bar\"), TodoObject(\"baz\")]\n print()\n\n.. code-block:: html\n\n

      The \"thing\" list

      ...
      • foo
      • bar
      • baz
      \n\n\nLet's pass this HTML to an HTML beautifier:\n\n.. code-block:: html\n\n
      \n

      The \"thing\" list

      \n
      \n \n \n \n
      \n
        \n
      • foo
      • \n
      • bar
      • \n
      • baz
      • \n
      \n
      \n\nAnd that's it, we have our todo-list app! To use it in a page, just create a component that will render the html base markup and integrate the ``TodoApp`` component in it. You don't even need a component:\n\n.. code-block:: python\n\n todos = [TodoObject(\"foo\"), TodoObject(\"bar\"), TodoObject(\"baz\")]\n\n print(\n \n \n \n \n \n )\n\nThe beautified output would be:\n\n.. code-block:: html\n\n \n\n \n
      \n

      The \"thing\" list

      \n
      \n \n \n \n
      \n
        \n
      • foo
      • \n
      • bar
      • \n
      • baz
      • \n
      \n
      \n \n\n \n\n\nOverriding a component\n======================\n\nWe have a generic todo-list, but following the available types of todo, we may want to have a \"todo-list\" and a \"thing-list\".\n\nWe already have the todo list because our ``TodoApp`` has a type of ``todo`` by default.\n\nSo let's create a ``ThingApp``.\n\n\nInheritance\n-----------\n\nThe first way of doing this is to inherit from our ``TodoApp``. But by inheriting we cannot remove props from the parent (it's not really true, we'll see this later), so we still have the ``type`` prop by default. But we don't want to accept anything else than \"thing\". So we can redefine the ``type`` prop like this:\n\n.. code-block:: python\n\n class ThingApp(TodoApp):\n class PropTypes:\n type: DefaultChoices = ['thing']\n\nLet's use this component:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n

      The \"thing\" list

      • foo
      \n\nIf we try to pass \"todo\" for the ``type`` props, it won't work:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropChoiceError:\n .type: `todo` is not a valid choice for this prop (must be in ['thing'])\n\nBut still, it's strange to be able to pass a type.\n\n\nParent components\n-----------------\n\nLet's try another way: A parent component. A component that does nothing else that doing things with its children and returning it. What we want here, is a component that will return a ``TodoApp`` with the ``type`` prop forced to \"thing\".\n\nLet's do this\n\n.. code-block:: python\n\n class ThingApp(Element):\n class PropTypes:\n todos: Required[List[TodoObject]]\n\n def render(self, context):\n return \n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n

      The \"thing\" list

      • foo
      \n\n\nIt works, and this time, we cannot pass the ``type`` prop:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropNameError: .type: is not an allowed prop\n\n\nPropTypes DRYness\n-----------------\n\nNotice how we had to define the type for the ``todos`` props. Both in ``TodoApp`` and ``TodoThing``.\n\nThere are many ways to handle that.\n\nThe first one would be to ignore the type in ``ThingApp`` because it will be checked in ``TodoApp``. So we'll use the type ``Any``:\n\n\n.. code-block:: python\n\n from typing import Any\n\n #...\n\n class ThingApp(Element):\n class PropTypes:\n todos: Any\n\n #...\n\n\nLet's try with a valid list of todos:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n

      The \"thing\" list

      ...
      • foo
      \n\n\nBut what if we pass something else?\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropValueError:\n .todos: `foo, bar` is not a valid value for this prop (type: , expected: List[TodoObject])\n\nIt works as expected but the error is reported at the ``TodoApp`` level, which is perfectly normal.\n\nAnother way would be to defined the type at a higher level:\n\n.. code-block:: python\n\n TodoObjects = Required[List[TodoObject]]\n\n class TodoApp(Element):\n class PropTypes:\n todos: TodoObjects\n # ...\n\n class ThingApp(Element):\n class PropTypes:\n todos: TodoObjects\n # ...\n\nNow if we pass something else, we have the error reported at the correct level:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropValueError:\n .todos: `foo, bar` is not a valid value for this prop (type: , expected: List[TodoObject])\n\n\nBut if you can't or don't want to do that, you can keep the type defined in ``TodoApp`` et use the ``prop_type`` class method of a component to get the type of a prop:\n\n.. code-block:: python\n\n class ThingApp(Element):\n class PropTypes:\n todos: TodoApp.prop_type(\"todos\")\n # ...\n\nBut does it really matter to have the error raised for ``ThingApp`` or ``TodoApp``? Because at the end, it's really ``TodoApp`` that have to do the check.\n\nSo this should be a way to do this in a more generic way..\n\n\nFunction\n--------\n\nWe saw earlier that a component can be a single function to render a component. It just have to return a component, an html tag. One difference with class components is that there is not ``PropTypes`` so no validation. But... it's exactly what we need.\n\nWe want our ``ThingApp`` to accept some props (the ``todos`` prop), and return a ``TodoApp`` with a specific ``type`` prop.\n\nSo we could do:\n\n.. code-block:: python\n\n def ThingApp(todos):\n return \n\nHere we can see that we cannot pass ``type`` to ``ThingsApp``, it is not a valid argument.\n\nLet's try it:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n

      The \"thing\" list

      ...
      • foo
      \n\n\nHere we have only one prop to pass so it's easy. But imagine if we have many ones. We can use the ``{**props}`` syntax:\n\n.. code-block:: python\n\n def ThingApp(**props):\n return \n\n\nAnd you can do with even fewer characters (if it counts):\n\n.. code-block:: python\n\n ThingApp = lambda **props: \n\n\nThese two fonctions behave exactly the same.\n\nAnd you cannot pass a ``type`` prop because it would be a python error, as it would be passed twice to ``TodoApp``:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n TypeError: BaseMetaclass object got multiple values for keyword argument 'type'\n\n\n(yes it talks about ``BaseMetaclass`` which is the metaclass that creates our components classes)\n\nAnd any other wrong props would be validated by ``TodoApp``:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropNameError: .foo: is not an allowed prop\n\nWith this in mind, we could have created a generic fonction that force the type of any component accepting a ``type`` prop:\n\n.. code-block:: python\n\n Thingify = lambda component, **props: \n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n

      The \"thing\" list

      ...
      • foo
      \n\n\nThe rendered component is ``TodoApp``, the ``type`` prop is \"thing\" and the other props (here only ``todos``) are correctly passed.\n\n\nHigher-order components\n-----------------------\n\nNow extend this concept to a more generic case: \"higher-order components\". In `React a \"high order component\" `_, is \"a function that takes a component and returns a new component.\"\n\n\nThe idea is:\n\n.. code-block:: python\n\n EnhancedComponent = higherOrderComponent(WrappedComponent)\n\nA classic way of doing it is to return a new component class:\n\n.. code-block:: python\n\n def higherOrderComponent(WrappedComponent):\n\n class HOC(Element):\n __display_name__ = f\"higherOrderComponent({WrappedComponent.__display_name__})\"\n\n class PropTypes(WrappedComponent.PropTypes):\n pass\n\n def render(self, context):\n return {self.childre()}\n\n return HOC\n\nNotice how we set the ``PropTypes`` class to inherit from the one of the wrapped component, and how we pass all the props to the wrapped component, along with the children. With the returned component will accept the same props, with the same types, as the wrapped one.\n\nAnd also notice the ``__display_name__``. It will be used in exceptions to let you now the component that raised it. Here, without forcing it, it would have been set to ``HOC``, which is not helpful. Instead, we indicate that it is a transformed version of the passed component.\n\nHere it is a function that does nothing useful.\n\nIn our example we could have done this:\n\n.. code-block:: python\n\n def thingify(WrappedComponent):\n\n class HOC(Element):\n __display_name__ = f\"thingify({WrappedComponent.__display_name__})\"\n\n class PropTypes(WrappedComponent.PropTypes):\n __exclude__ = {'type'}\n\n def render(self, context):\n return {self.children()}\n\n return HOC\n\n\nTwo important things here:\n\n- notice how we use ``__exclude__ = {'type'}`` to remove the ``type`` prop from the ones we inherit from ``WrappedComponent.PropTypes``. So the returned component will expect the exact same props as the wrapped one, except for ``type``.\n- we added ``{self.children()}`` in the rendered wrapped component, because even if we actually know that the component we'll wrap, ``TodoApp``, doesn't take children (it could but it does nothing with them), we cannot say in advance that it will always be the case, and also that this higher-order component won't be used to wrap another component than ``TodoApp``. So it's better to always do this.\n\nAnd now we can create our ``ThingApp``:\n\n.. code-block:: python\n\n ThingApp = thingify(TodoApp)\n\n\nAnd use it:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n

      The \"thing\" list

      ...
      • foo
      \n\n\nIf we try to pass the type:\n\n.. code-block:: python\n\n print()\n\n\n.. code-block:: python\n\n mixt.exceptions.InvalidPropNameError: .type: is not an allowed prop\n\n\nSo as planned, we cannot pass the type. And notice how the ``__display_name__`` is used.\n\n\nLet's think about how powerful this is.\n\nLet say we want to keep our ``TodoApp`` take a list of ``TodoObject``. But we want to get them from a \"source\".\n\nWe can even directly write it this new higher-order-component in a generic way:\n\n.. code-block:: python\n\n def from_data_source(WrappedComponent, prop_name, get_source):\n\n class HOC(Element):\n __display_name__ = f\"from_data_source({WrappedComponent.__display_name__})\"\n\n class PropTypes(WrappedComponent.PropTypes):\n __exclude__ = {prop_name}\n\n def render(self, context):\n props = self.props.copy()\n props[prop_name] = get_source(props, context)\n return {self.children()}\n\n return HOC\n\n\nThis time, the function ``from_data_source`` takes two arguments in addition to the ``WrappedComponent``:\n\n- ``prop_name``: it's the name of the prop of the wrapped component to fill with some data\n- ``get_source``: it's a function that will be called to get the data\n\nLook how we inherited the ``PropTypes`` from the wrapped component and how we excluded ``prop_name``. So we don't have (and can't) pass the data to our new component.\n\nAnd then in ``render``, we set a prop to pass to ``WrappedComponent`` with the result of a call to ``get_source``.\n\nSo let's write a very simple function (this could be a complicated one with caching, filtering...) that take the props and the context, and returns some data:\n\n.. code-block:: python\n\n def get_todos(props, context):\n # here it could be a call to a database\n return [\n TodoObject(\"fooooo\"),\n TodoObject(\"baaaar\"),\n ]\n\n\nAnd we can compose our component:\n\n.. code-block:: python\n\n SourcedTodoApp = from_data_source(TodoApp, 'todos', get_todos)\n ThingApp = thingify(SourcedTodoApp)\n\n\nAnd run it:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: html\n\n

      The \"thing\" list

      ...
      • fooooo
      • baaaar
      \n\n\nIt works as expected, and the data is fetched only when the component needs to be rendered.\n\n\nContext\n=======\n\nSo, we have a todo list, that can fetch data from an external source. But we may want the data to be different depending on the user.\n\nWhat we can do, it's at the main level, get our user and passing it on every component to be sure that each component is able to get the current logged in user.\n\nWouldn't it be cumbersome?\n\nSolving this use case is the exact purpose of the ``Context`` concept provided by ``mixt``. It is, of course, `inspired by the concept of context in React `_.\n\nAnd as they said:\n\n Context is designed to share data that can be considered \u201cglobal\u201d for a tree of React components, such as the current authenticated user, theme, or preferred language.\n\nCreating a context is as simple as creating a component, except that it will inherits from ``BaseContext`` and doesn't need a ``render`` method (it will render its children).\n\nAnd it takes a ``PropTypes`` class, that define the types of data the context will accept and pass down the tree.\n\nSo let's create our context that will hold the id of the authenticated user.\n\n.. code-block:: python\n\n from mixt import BaseContext\n\n class UserContext(BaseContext):\n class PropTypes:\n authenticated_user_id: Required[int]\n\n\nNow, we want to update our ``get_todos`` method to take the ``authenticated_user_id`` into account.\n\nRemember, we passed it the props and the context. The context will be useful here:\n\n.. code-block:: python\n\n def get_todos(props, context):\n return {\n 1:[\n TodoObject(\"1-1\"),\n TodoObject(\"1-2\"),\n ],\n 2: [\n TodoObject(\"2-1\"),\n TodoObject(\"2-2\"),\n ]\n }[context.authenticated_user_id]\n\n\nAnd now we can render our app with the context:\n\n.. code-block:: python\n\n print(\n \n \n \n )\n\n.. code-block:: python\n\n

      The \"thing\" list

      ...
      • 1-1
      • 1-2
      \n\n\nWe can see the todo entries for the user 1.\n\nLet's try with the user 2:\n\n.. code-block:: python\n\n print(\n \n \n \n )\n\n.. code-block:: python\n\n

      The \"thing\" list

      ...
      • 2-1
      • 2-2
      \n\nWe can see the todo entries for the user 2.\n\nIn this case of course we could have passed the user id as a prop. But imagine the todo app being deep in the components tree, it's a lot easier to pass it this way.\n\nBut as said in the React documentation:\n\n Don\u2019t use context just to avoid passing props a few levels down. Stick to cases where the same data needs to be accessed in many components at multiple levels.\n\nWhen there is no context, the ``context`` argument of the ``render`` method is set to ``EmptyContext`` and not to ``None``. So you can directly use the ``has_prop`` method to check if a prop is available via the context.\n\nLet's update the ``get_todos`` functions to return an empty list of todo objects if there is not authenticated user.\n\n.. code-block:: python\n\n def get_todos(props, context):\n if not context.has_prop('authenticated_user_id') or not context.authenticated_user_id:\n return []\n return {\n 1:[\n TodoObject(\"1-1\"),\n TodoObject(\"1-2\"),\n ],\n 2: [\n TodoObject(\"2-1\"),\n TodoObject(\"2-2\"),\n ]\n }[context.authenticated_user_id]\n\nLet's try this:\n\n.. code-block:: python\n\n print()\n\n.. code-block:: python\n\n

      The \"thing\" list

      ...
        \n\n\nAnd it still works with a user in the context:\n\n.. code-block:: python\n\n print(\n \n \n \n )\n\n.. code-block:: python\n\n

        The \"thing\" list

        ...
        • 1-1
        • 1-2
        \n\n\n**Important note about contexts**: you can have many contexts! But defining the same prop in many contexts may lead to undefined behaviour.\n\n\nStyle and Javascript\n====================\n\nEverybody loves a beautiful design, and maybe some interaction.\n\nIt is easily doable: we generate HTML and HTML can contains some CSS and JS.\n\nLet's add some interaction first: when adding an item in the ``TodoForm``, let's add it to the list.\n\nFirst we add in our ``TodoForm`` component a ``render_javascript`` method that will host our (bad, we could do better but it's not the point) javascript:\n\n.. code-block:: python\n\n class TodoForm(Element):\n # ...\n\n def render_javascript(self, context):\n return html.Raw(\"\"\"\n function on_todo_add_submit(form) {\n var text = form.todo.value;\n alert(text);\n }\n \"\"\")\n\nTo start we only display the new todo text.\n\nNow update our ``render`` method to return this javascript (note that the use of a ``render_javascript`` method is only to separate concerns, it could have been in the ``render`` method directly.\n\n.. code-block:: python\n\n class TodoForm(Element):\n # ...\n\n def render(self, context):\n # ...\n\n return \\\n \n \n
        \n \n \n \n
        \n\nNotice the ``Fragment`` tag. It's a way to encapsulate many elements to be returned, like in React. It could have been a simple list but with comas at the end:\n\n.. code-block:: python\n\n return [\n ,\n
        \n ...\n
        \n ]\n\nNow we want to add an item to the list. It's not the role of the ``TodoForm`` to do this, but to the list. So we'll add some JS in the ``TodoList`` component: a function that take some text and create a new entry.\n\nAs for ``TodoForm``, we add a ``render_javascript`` method with (still bad) javascript:\n\n.. code-block:: python\n\n class TodoList(Element):\n # ...\n\n def render_javascript(self, context):\n\n todo_placeholder = \n\n return html.Raw(\"\"\"\n TODO_TEMPLATE = \"%s\";\n function add_todo(text) {\n var html = TODO_TEMPLATE.replace(\"placeholder\", text);\n var ul = document.querySelector('#todo-items');\n ul.innerHTML = html + ul.innerHTML;\n }\n \"\"\" % (todo_placeholder))\n\nAnd we update our ``render`` method to add the ``\n
          {[ for todo in self.todos]}
        \n \n\nAnd now we can update the ``render_javascript`` method of the ``TodoForm`` component to use our new ``add_toto`` javascript function:\n\n\n.. code-block:: python\n\n class TodoForm(Element):\n # ...\n\n def render_javascript(self, context):\n return html.Raw(\"\"\"\n function on_todo_add_submit(form) {\n var text = form.todo.value;\n add_todo(text);\n }\n \"\"\")\n\nAnd that's all. Nothing special, in fact.\n\nBut let's take a look at the output of ou ``TodoApp``:\n\n.. code-block:: python\n\n print(\n \n \n \n )\n\nThe beautified output is:\n\n.. code-block:: html\n\n
        \n

        The \"thing\" list

        \n \n
        \n \n \n \n
        \n \n
          \n
        • 1-1
        • \n
        • 1-2
        • \n
        \n
        \n\nSo we have many ``script`` tag. It could be great to have only one.\n\nCollectors\n----------\n\n``mixt`` comes with a way to \"collect\" parts of what is rendered to put them somewhere else. We have at our disposal two simple collectors, to be used as components: ``JSCollector`` and ``CSSCollector``.\n\nThese components collect parts of their children tree.\n\nCollector.Collect\n^^^^^^^^^^^^^^^^^\n\nThe first way is by using the collector ``Collect`` tag.\n\nFirst let's change our main call:\n\n.. code-block:: python\n\n from mixt import JSCollector\n\n print(\n \n \n \n \n \n )\n\nThis will collect the content of all the ``JSCollector.Collect`` tag.\n\nLet's update our ``TodoForm`` and replace our ``script`` tag by a ``JSCollector.Collect`` tag:\n\n.. code-block:: python\n\n class TodoForm(Element):\n # ...\n\n def render(self, context):\n\n if callable(self.add_url):\n add_url = self.add_url(self.type)\n else:\n add_url = self.add_url\n\n return \\\n {self.render_javascript(context)}\n
        \n \n \n \n \n\n\nWe can do the same with the ``TodoList``:\n\n.. code-block:: python\n\n class TodoList(Element):\n # ...\n\n def render(self, context):\n return \\\n \n {self.render_javascript(context)}\n
          {[ for todo in self.todos]}
        \n
        \n\n\nNow let's run our updated code:\n\n.. code-block:: python\n\n print(\n \n \n \n \n \n )\n\nThe beautified output is:\n\n.. code-block:: html\n\n
        \n

        The \"thing\" list

        \n
        \n \n \n \n
        \n
          \n
        • 1-1
        • \n
        • 1-2
        • \n
        \n
        \n \n\nAs you can see, all the scripts are in a single ``script`` tag, at the end. More precisely, at the end of where the ``JSCollector`` tag was, because we used ``render_position=\"after\"``. Another possibility is ``render_position=\"before\"`` to put this where the ``JSCollector`` tag started.\n\nAll of this work exactly the same way for the ``CSSCollector`` tag, where content is put in a ``