# 1.0.0 Porting Guide

The 0.1 through 1.0.0 releases focused on bringing in functions from yum and python-fedora. This porting guide tells how to port from those APIs to their kitchen replacements.

## python-fedora

python-fedora | kitchen replacement
`fedora.iterutils.isiterable()` | `kitchen.iterutils.isiterable()` [1]
`fedora.textutils.to_unicode()` | `kitchen.text.converters.to_unicode()`
`fedora.textutils.to_bytes()` | `kitchen.text.converters.to_bytes()`

[1]

`isiterable()` has changed slightly in kitchen. The `include_string` attribute has switched its default value from `True` to `False`. So you need to change code like:

    >>> # Old code
    >>> isiterable('abcdef')
    True
    >>> # New code
    >>> isiterable('abcdef', include_string=True)
    True

## yum

yum | kitchen replacement
`yum.i18n.dummy_wrapper()` | `kitchen.i18n.DummyTranslations.ugettext()` [2]
`yum.i18n.dummyP_wrapper()` | `kitchen.i18n.DummyTanslations.ungettext()` [2]
`yum.i18n.utf8_width()` | `kitchen.text.display.textual_width()`
`yum.i18n.utf8_width_chop()` | `kitchen.text.display.textual_width_chop()` and `kitchen.text.display.textual_width()` [3] [5]
`yum.i18n.utf8_valid()` | `kitchen.text.misc.byte_string_valid_encoding()`
`yum.i18n.utf8_text_wrap()` | `kitchen.text.display.wrap()` [4]
`yum.i18n.utf8_text_fill()` | `kitchen.text.display.fill()` [4]
`yum.i18n.to_unicode()` | `kitchen.text.converters.to_unicode()` [6]
`yum.i18n.to_unicode_maybe()` | `kitchen.text.converters.to_unicode()` [6]
`yum.i18n.to_utf8()` | `kitchen.text.converters.to_bytes()` [6]
`yum.i18n.to_str()` | `kitchen.text.converters.to_unicode()` or `kitchen.text.converters.to_bytes()` [7]
`yum.i18n.str_eq()` | `kitchen.text.misc.str_eq()`
`yum.misc.to_xml()` | `kitchen.text.converters.unicode_to_xml()` or `kitchen.text.converters.byte_string_to_xml()` [8]
`yum.i18n._()` | See: Initializing Yum i18n
`yum.i18n.P_()` | See: Initializing Yum i18n
`yum.i18n.exception2msg()` | `kitchen.text.converters.exception_to_unicode()` or `kitchen.text.converter.exception_to_bytes()` [9]

[2] (1,2)

These yum methods provided fallback support for `gettext` functions in case either `gaftonmode` was set or `gettext` failed to return an object. In kitchen, we can use the `kitchen.i18n.DummyTranslations` object to fulfill that role. Please see Initializing Yum i18n for more suggestions on how to do this.

[3]

The yum version of these functions returned a byte `str`. The kitchen version listed here returns a `unicode` string. If you need a byte `str` simply call `kitchen.text.converters.to_bytes()` on the result.

[4] (1,2)

The yum version of these functions would return either a byte `str` or a `unicode` string depending on what the input value was. The kitchen version always returns `unicode` strings.

[5]

`yum.i18n.utf8_width_chop()` performed two functions. It returned the piece of the message that fit in a specified width and the width of that message. In kitchen, you need to call two functions, one for each action:

    >>> # Old way
    >>> utf8_width_chop(msg, 5)
    (5, 'く ku')
    >>> # New way
    >>> from kitchen.text.display import textual_width, textual_width_chop
    >>> (textual_width(msg), textual_width_chop(msg, 5))
    (5, u'く ku')

[6] (1,2,3)

If the yum version of `to_unicode()` or `to_utf8()` is given an object that is not a string, it returns the object itself. `kitchen.text.converters.to_unicode()` and `kitchen.text.converters.to_bytes()` default to returning the `simplerepr` of the object instead. If you want the yum behaviour, set the `nonstring` parameter to `passthru`:

    >>> from kitchen.text.converters import to_unicode
    >>> to_unicode(5)
    u'5'
    >>> to_unicode(5, nonstring='passthru')
    5

[7]

`yum.i18n.to_str()` could return either a byte `str`. or a `unicode` string In kitchen you can get the same effect but you get to choose whether you want a byte `str` or a `unicode` string. Use `to_bytes()` for `str` and `to_unicode()` for `unicode`.

[8]

`yum.misc.to_xml()` was buggy as written. I think the intention was for you to be able to pass a byte `str` or `unicode` string in and get out a byte `str` that was valid to use in an xml file. The two kitchen functions `byte_string_to_xml()` and `unicode_to_xml()` do that for each string type.

[9]

When porting `yum.i18n.exception2msg()` to use kitchen, you should setup two wrapper functions to aid in your port. They’ll look like this:

    from kitchen.text.converters import EXCEPTION_CONVERTERS, \
        BYTE_EXCEPTION_CONVERTERS, exception_to_unicode, \
        exception_to_bytes
    def exception2umsg(e):
        '''Return a unicode representation of an exception'''
        c = [lambda e: e.value]
        c.extend(EXCEPTION_CONVERTERS)
        return exception_to_unicode(e, converters=c)
    def exception2bmsg(e):
        '''Return a utf8 encoded str representation of an exception'''
        c = [lambda e: e.value]
        c.extend(BYTE_EXCEPTION_CONVERTERS)
        return exception_to_bytes(e, converters=c)

The reason to define this wrapper is that many of the exceptions in yum put the message in the `value` attribute of the `Exception` instead of adding it to the `args` attribute. So the default `EXCEPTION_CONVERTERS` don’t know where to find the message. The wrapper tells kitchen to check the `value` attribute for the message. The reason to define two wrappers may be less obvious. `yum.i18n.exception2msg()` can return a `unicode` string or a byte `str` depending on a combination of what attributes are present on the `Exception` and what locale the function is being run in. By contrast, `kitchen.text.converters.exception_to_unicode()` only returns `unicode` strings and `kitchen.text.converters.exception_to_bytes()` only returns byte `str`. This is much safer as it keeps code that can only handle `unicode` or only handle byte `str` correctly from getting the wrong type when an input changes but it means you need to examine the calling code when porting from `yum.i18n.exception2msg()` and use the appropriate wrapper.

### Initializing Yum i18n

Previously, yum had several pieces of code to initialize i18n. From the toplevel of `yum/i18n.py`:

    try:.
        '''
        Setup the yum translation domain and make _() and P_() translation wrappers
        available.
        using ugettext to make sure translated strings are in Unicode.
        '''
        import gettext
        t = gettext.translation('yum', fallback=True)
        _ = t.ugettext
        P_ = t.ungettext
    except:
        '''
        Something went wrong so we make a dummy _() wrapper there is just
        returning the same text
        '''
        _ = dummy_wrapper
        P_ = dummyP_wrapper

With kitchen, this can be changed to this:

    from kitchen.i18n import easy_gettext_setup, DummyTranslations
    try:
        _, P_ = easy_gettext_setup('yum')
    except:
        translations = DummyTranslations()
        _ = translations.ugettext
        P_ = translations.ungettext

Note

In Overcoming frustration: Correctly using unicode in python2, it is mentioned that for some things (like exception messages), using the byte `str` oriented functions is more appropriate. If this is desired, the setup portion is only a second call to `kitchen.i18n.easy_gettext_setup()`:

    b_, bP_ = easy_gettext_setup('yum', use_unicode=False)

The second place where i18n is setup is in `yum.YumBase._getConfig()` in `yum/__init_.py` if `gaftonmode` is in effect:

    if startupconf.gaftonmode:
        global _
        _ = yum.i18n.dummy_wrapper

This can be changed to:

    if startupconf.gaftonmode:
        global _
        _ = DummyTranslations().ugettext()