понедельник, 29 августа 2011 г.

Bottle application helper

Next is the helper class for creation of the Bottle Web Framework applications:
def expose(path, **kw):
    def decor(view):
        view._route_path_ = path
        view._kwargs_ = kw
        return view
    return decor

class BottleBaseApp(object):
    """
    - Autosave attributes with names from __session__ attribute
    - Session type is attr __session_type__ ('cookie'|'pickle'),
      'cookie' is default
    - Methods decorated with @expose will be auto-routed and
      sessions' attrs will be flushed at the end
    """
    def __init__(self, bottleapp):
        self.bottle = bottleapp
        # Prepare session manager as attr sessions
        if self.__session_type__ == "pickle":
            self.sessions = PickleSession()
        else:
            self.sessions = CookieSession()

        def router(meth):
            """Return wrapped view-method"""
            # meth is already bound to self
            def wrp(f=meth, *a, **kw):
                try:
                    ret = f(*a, **kw) # bound, no needs self,
                finally:
                    self.flush_session()
                return ret
            functools.update_wrapper(wrp, meth)
            return wrp

        # Route all decorated methods
        for attrname in dir(self):
            attr = getattr(self, attrname)
            path = getattr(attr, "_route_path_", None)
            kw = getattr(attr, "_kwargs_", {})
            if path:
                self.bottle.route(path, callback=router(attr), **kw)

    def flush_session(self):
        """Copy to session all attrs and save session"""
        try:
            ses = self.sessions.get_session()
            for a in self.__session__:
                try:
                    v = getattr(self, a)
                    ses[a] = v
                except:
                    continue
            self.sessions.save(ses)
        except Exception as x:
            pass
And now is the example how to use this:
class MyApp(BottleBaseApp):
    __session_type__ = "cookie"
    __session__ = ["tasks_ctl_res", "tasks_prc", "prc_conns",]

    def __init__(self, bottleapp):
        super(MyApp, self).__init__(bottleapp)
        self.tasks_ctl_res = {}
        self.tasks_prc = []
        self.prc_conns = {}


    @expose("/index")
    def index(self):
        return "Index page is empty"

    # ... etc ...
I use Session implementation by Sean Reifschneider - MANY THANKS! - (see this).

воскресенье, 21 августа 2011 г.

Internationalization in the Bottle

This approach is very limited! In serious sites you have to support Web-robots, links with language code... I use it for small (embedded Web console) projects only.

I18N in the Bottle Python framework is easy. I prefer to do i18n for 2 different tasks:
  1. i18n for messages (strings)
  2. i18n for static text big pies (in the templates)
I use gettext mechanism. In my application folder I have folders:
locale/
    en/
      TEMPLATES/
        xxx.tpl
    bg/
      LC_MESSAGES/
        messages.mo
      TEMPLATES/
        xxx.tpl
    xx/
      LC_MESSAGES/
        messages.mo
      TEMPLATES/
        xxx.tpl
bg_messages.po
xx_messages.po

"locale" is the folder for i18n/i10n. Folders with language codes ("bg", "xx") are placed here. Each of them contents two subfolders: LC_MESSAGES with compiled .po file (messages.mo) and TEMPLAES (with .tpl files), except en/ folder: no LC_MESSAGES here.

I use make-like mechanism for compilation .po-files, adding new locale - Python fabricate build tool. It's very lightweight and is written in single Python file!

.po-files are very simple. I changed next strings in the one:
"Content-Type: text/plain; charset=utf8\n"
"Content-Transfer-Encoding: utf8\n"
and encoded it into utf-8 (using vim, for example). Then added my msgid, msgstr pairs, as usual.

So, my Bottle application have to know current language (preferred by user) and how to: a) found corresponding template b) how to translate word/text.
# to test, change language in the browser (see priority of them!). Then
# refresh page - you should see text in different language
_LANGS = {}
def initi18n():
    """Init internationalization of UI
    """
    import __builtin__
    global _LANGS
    for l in ("bg","ru"): # list of supported language
        trans = gettext.translation(I18N_DOMAIN, LOCALEDIR, languages=[l])
        _LANGS[l] = trans
    # default translator (i.e. _())
    __builtin__.__dict__["_"] = unicode


def request_lang(req):
    """Determine preferred language of the user from WSGI environment
    variable HTTP_ACCEPT_LANGUAGE. Default is 'en'
    """
    # if cfg module overrides user language
    if cfg.LANG != "default":
        return cfg.LANG

    try:
        return req.environ.get("HTTP_ACCEPT_LANGUAGE", "").split(";")[0].split("-")[0]
    except:
        return "en"


def switch_lang(lang):
    """Select language for output VIA GETTEXT
    """
    import __builtin__
    trans = _LANGS.get(lang, None)
    if trans:
        trans.install(unicode=True)
    else:
        __builtin__.__dict__["_"] = unicode


def templatei18n(name, lang=None, **kw):
    """Like bottle template() but render for current language
    from os.environ["LANGUAGE"]. And not like bottle's template,
    doesn't support source, only name (not path!) as 1st arg!
    If no lang, it will be selected from request
    """
    if not lang:
        lang = request_lang(request)
    tplpath = "%s/%s/TEMPLATES/%s" % (LOCALEDIR, lang, name)
    tplpath = os.path.abspath(tplpath) + ".tpl"
    if not os.path.exists(tplpath):
        tplpath = name
    return template(tplpath, **kw)


def i18n(templ):
    """Decorator for template rendering using current
    language (i18n support)
    """
    def decor(view):
        @functools.wraps(view)
        def w(*a, **kw):
            lang = request_lang(request)
            switch_lang(lang)
            d = view(*a, **kw)
            return templatei18n(templ, lang=lang, **d)
        return w
    return decor

I use initi18n() before application starting and use @i18n decorator for localized templates. Nothing else:
@i18n("tasks") # name of the template
def tasks_list(self):
    """Show table of tasks
    """
    ps = tasks_info()
    for taskname in ps:
        msg = self.runres.get(taskname, "") or ps[taskname]["msg"]
        ps[taskname]["msg"] = msg
        self.runres[taskname] = ""
    return dict(ps=ps)
User should select preferred language in the Web browser options. You - create template "tasks.tpl" in all locale/*/TEMPLATES and translated messages in .mo-file to use _(..) function everywhere you need (Python code, templates).

четверг, 23 июня 2011 г.

Usage of UART for debug messages on AVR

If you need to print out a debug messages to UART port of AVR chip, you can use a simple way for it: switch stdout stream to UART. This is the code of the solution:
#include <avr/io.h>
#include <stdio.h>

#ifndef NDEBUG

#  if !defined(F_DEBUG_UART) && !defined(F_CPU)
#    error F_DEBUG_UART frequency or F_CPU frequency should be defined!
#  endif

#  ifndef F_DEBUG_UART
#    define F_DEBUG_UART F_CPU
#  endif

#  ifndef DEBUG_NUART
#    error DEBUG_NUART should be defined!
#  endif

#  define __CONC3(X, Y, Z) X ## Y ## Z
#  define _CONC3(X, Y, Z) __CONC3(X, Y, Z)
// _CONC3(UDRE, DEBUG_NUART,) does not works
#  define _UDRE (_CONC3(UDR, E, DEBUG_NUART))
#  define _UDR (_CONC3(UDR, DEBUG_NUART,))
#  define _UBRRH (_CONC3(UBRR, DEBUG_NUART, H))
#  define _UBRRL (_CONC3(UBRR, DEBUG_NUART, L))
#  define _UCSRA (_CONC3(UCSR, DEBUG_NUART, A))
#  define _UCSRB (_CONC3(UCSR, DEBUG_NUART, B))
#  define _UCSRC (_CONC3(UCSR, DEBUG_NUART, C))
#  define _RXEN (_CONC3(RXEN, DEBUG_NUART,))
#  define _TXEN (_CONC3(TXEN, DEBUG_NUART,))
#  define _UCSZ0 (_CONC3(UCSZ, DEBUG_NUART, 0))
#  define _U2X (_CONC3(U2X, DEBUG_NUART,))
#  define DEBUG_UART_SUPPORT() \
static int debug_uart_putchar(char c, FILE *stream); \
static FILE mystdout = FDEV_SETUP_STREAM(debug_uart_putchar, NULL, _FDEV_SETUP_WRITE); \
static int debug_uart_putchar(char c, FILE *stream) \
{ \
        while (0 == (_UCSRA & (1<<_UDRE))) ; \
        _UDR = c; \
        return (0); \
}

/* 8 bits data, 1 stop bit, 9600 kbps, async */
#  define INIT_DEBUG_UART() do { \
        stdout = &mystdout; \
        _UCSRC = 0x06; \
        _UCSRA = 0x00; \
        _UCSRB = 0x18; /* receive, transmit, no interrupts */ \
        uint16_t ubrr = (F_DEBUG_UART/16/9600-1); \
        _UBRRL = (unsigned char)ubrr; \
        _UBRRH = (unsigned char)(ubrr>>8); \
} while (0)

#  define DEBUG_UART_PRN(...) printf(__VA_ARGS__)

#else
#  define DEBUG_UART_SUPPORT()
#  define INIT_DEBUG_UART()
#  define DEBUG_UART_PRN(...)
#endif
Save it into debug_uart.h, for example.
What you need to use it, it's only defining F_DEBUG_UART or F_CPU, DEBUG_NUART and calling 3 macros: DEBUG_UART_SUPPORT() on module level (it generates code, so call it only once!), INIT_DEBUG_UART() to init UART and DEBUG_UART_PRN(...) where you need message printing. The example is:
#define F_DEBUG_UART 7372800
#define DEBUG_NUART 0
#include "debug_uart.h"
DEBUG_UART_SUPPORT();
int
main(void)
{
        INIT_DEBUG_UART();
        DEBUG_UART_PRN("Hello world, %d\n", 99);
        ... so on ...
F_DEBUG_UART is the exact amount of the system oscillator frequency to calculate UART baud rate. Default value is equals to F_CPU, but it's possible, that you have value of fosc = 7.3728MHz (see AVR Datasheet about "Examples of UBRR settings") and you use in the Makefile F_CPU value as 8MHz - if you use 8MHz for UBRR calculation too, you will get errors in transmission! So use for F_DEBUG_UART exact values only!
DEBUG_NUART defined number of UART. If you define NDEBUG, all macros will be empty - you don't need to delete it's calls. For more information see help on WinAVR.

вторник, 21 июня 2011 г.

Parsing of paths

Paths are often used in software environments: in file systems, in configuration files, in different IDs and so on. Here are two Python functions, used for parsing of them, they generate tracks from paths (function dothier()) and map based trees (function dottree()). Here you are:
def dothier(seq, sep='.'):
    """Generate path from dot-strings in order for creation
    tree: first - parents then children:
    >>> for x in dothier(["x.y.z", "a"]): print x, x.leaf
    a True
    x False
    x.y False
    x.y.z True

    Every returned string (unicode) has flag - leaf
    """
    # returns this, as unicode but with flag leaf
    _Node = type("dothier_node", (unicode,), {"leaf":False})

    seq = sorted(seq)
    mem = set()
    for p in seq:
        spl = p.split(sep)
        for ip in xrange(1, len(spl) + 1):
            pp = _Node(sep.join(spl[:ip]))
            if len(spl) == 1 or ip == len(spl):
                pp.leaf = True
            if not pp in mem:
                yield pp
                mem.add(pp)

def dottree(seq, sep='.', fullpath=False):
    """Returns dict of dicts like tree from sequence of dot separated strings.
    For example,
    >>> dottree(["x.y.z", "x.y", "a", "a.b.c", "a.b"])
    {'a': {'b': {'c': {}}}, 'x': {'y': {'z': {}}}}

    and

    >>> dottree(["x.y.z", "x.y", "a", "a.b.c", "a.b"], fullpath=True)
    {'a': {'a.b': {'a.b.c': {}}}, 'x': {'x.y': {'x.y.z': {}}}}
    """
    def ins(map, path):
        d = map
        parents = []
        for p in path:
            if fullpath:
                key = sep.join(parents + [p])
            else:
                key = p
            d = d.setdefault(key, {})
            parents.append(p)
    ret = {}
    seq = sorted(seq)
    for p in seq:
        pp = p.split(sep)
        if not pp:
            continue
        ins(ret, pp)
    return ret
Examples of usage are... in it's docstrings :) They are useful in GUI application: to construct tree or another hierarchical controls from paths (retrieved from config. files, for example).
This code is from my project "HMI pybase" (see Projects).