========================
Thursday, March 24, 2016
========================


:func:`super` in the :meth:`__str__`  of a `python_2_unicode_compatible`
========================================================================

Here is now how to reproduce :ticket:`844`.

The following code snippet was used to reproduce :ticket:`844`:

>>> from lino import startup
>>> startup('lino_welfare.projects.std.settings.doctests')
>>> from lino.api.doctest import *
>>> obj = households.Member()
>>> print(obj)
Member object

Explanation starts in :mod:`lino_xl.lib.households.models` which
defines::

    @python_2_unicode_compatible
    class Member(mixins.DatePeriod):
        def __str__(self):
            if self.person_id is None:
                return super(Member, self).__str__()
            if self.role is None:
                return unicode(self.person)
            return u"%s (%s)" % (self.person, self.role)

This code, on its own, is not problematic. The problem comes only when
Lino Welfare extends the `Member` model.  In
:mod:`lino_welfare.modlib.households.models` it says::

    class Member(Member, mixins.Human, mixins.Born):
        ...

And in :mod:`lino.mixins.human` we have::
    
    from lino_xl.lib.households.models import *

    @python_2_unicode_compatible
    class Human(model.Model):

        def __str__(self):
            return self.get_full_name(nominative=True)


The rule of thumb is: **Don't use :func:`super` in the :meth:`__str__`
method of a `python_2_unicode_compatible` model.**

My explanation is that `python_2_unicode_compatible` causes something
to get messed up with the *mro* for the :meth:`__str__` method, but I
wont't dive deeper into this right now because my problem was fixed by
changing the relevant line::

    return super(Member, self).__str__()

into an explicit copy of the code which I want to run there (defined
in the :func:`super` of Django's :class:`Model` class)::

    return str('%s object' % self.__class__.__name__)

I added a section about "Member objects" in
:ref:`welfare.specs.households` to cover it.


Moving from `fabric` to `invoke`
================================

I wanted to get the Lino test suite pass on Travis CI. It took me
almost the whole day.  Lots of subtle changes in :mod:`atelier`. 

The main problem was to understand `how invoke does configuration
<http://docs.pyinvoke.org/en/0.12.2/concepts/configuration.html>`_ and
to decide how to store `current_project` and how to define
configration variables.
I added an `invoke.yaml` to most projects.

Many more :cmd:`inv` commands now work.

:cmd:`fab bd` was the easiest: it simply wasn't yet imported into the
:mod:`atelier.tasks` module.

There was a bug in :func:`atelier.utils.confirm` which made it fail
under Python 2 when the prompt was a unicode string with non-ascii
characters.


.. literalinclude:: 0324.py

Above script gives::

    Traceback (most recent call last):
      File "0324.py", line 4, in <module>
        confirm("Ein schöner Tag?")
      File "/atelier/atelier/utils.py", line 213, in confirm
        ln = input(prompt)
    UnicodeEncodeError: 'ascii' codec can't encode character u'\xf6' in position 7: ordinal not in range(128)

A simplified version:

.. literalinclude:: 0324.py


But finally I can not do::

  $ pp inv clean
  $ pp inv prep test bd pd


I released Atelier 0.0.20 to PyPI (needed because Travis CI uses the
released version)

And the reward: the Lino test suite now `passes on Travis CI
<https://travis-ci.org/lsaffre/lino/builds/118242460>`_ using `invoke`
instead of `fabric`! At least for Python 2.