| 
 # Authors:  
#   Pavel Zuna <pzuna@redhat.com>  
#  
# Copyright (C) 2009  Red Hat  
# see file 'COPYING' for use and warranty information  
#  
# This program is free software; you can redistribute it and/or modify  
# it under the terms of the GNU General Public License as published by  
# the Free Software Foundation, either version 3 of the License, or  
# (at your option) any later version.  
#  
# This program is distributed in the hope that it will be useful,  
# but WITHOUT ANY WARRANTY; without even the implied warranty of  
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
# GNU General Public License for more details.  
#  
# You should have received a copy of the GNU General Public License  
# along with this program.  If not, see <http://www.gnu.org/licenses/>.  
"""  
Encoding capabilities.  
"""  
  
from decimal import Decimal  
  
class EncoderSettings(object):  
    """  
    Container for encoder settings.  
    """  
    encode_to = 'utf-8'  
    encode_none = False  
    encode_dict_keys = False  
    encode_dict_keys_postprocess = True  
    encode_dict_vals = True  
    encode_dict_vals_postprocess = True  
    encode_postprocessor = staticmethod(lambda x: x)  
  
    decode_from = 'utf-8'  
    decode_none = False  
    decode_dict_keys = False  
    decode_dict_keys_postprocess = True  
    decode_dict_vals = True  
    decode_dict_vals_postprocess = True  
    decode_dict_vals_table = dict()  
    decode_dict_vals_table_keygen = staticmethod(lambda x, y: x)  
    decode_postprocessor = staticmethod(lambda x: x)  
  
  
class Encoder(object):  
    """  
    Base class implementing encoding of python scalar types to strings  
    and vise-versa.  
    """  
  
    encoder_settings = EncoderSettings()  
  
    def __init__(self):  
        # each instance should have its own settings  
        self.encoder_settings = EncoderSettings()  
  
    def _decode_dict_val(self, key, val):  
        f = self.encoder_settings.decode_dict_vals_table.get(  
            self.encoder_settings.decode_dict_vals_table_keygen(key, val)  
        )  
        if f:  
            return val  
        return self.decode(val)  
  
    def encode(self, var):  
        """  
        Encode any python built-in python type variable into `self.encode_to`.  
  
        Compound types have their individual members encoded.  
  
        Returns an encoded copy of 'var'.  
        """  
        if isinstance(var, str):  
            return var  
        elif isinstance(var, unicode):  
            return self.encoder_settings.encode_postprocessor(  
                var.encode(self.encoder_settings.encode_to)  
            )  
        elif isinstance(var, bool):  
            if var:  
                var = 'TRUE'  
            else:  
                var = 'FALSE'  
            return self.encoder_settings.encode_postprocessor(  
                var.encode(self.encoder_settings.encode_to)  
            )  
        elif isinstance(var, (float, Decimal, int, long)):  
            return self.encoder_settings.encode_postprocessor(  
                str(var).encode(self.encoder_settings.encode_to)  
            )  
        elif isinstance(var, list):  
            return [self.encode(m) for m in var]  
        elif isinstance(var, tuple):  
            return tuple(self.encode(m) for m in var)  
        elif isinstance(var, dict):  
            if self.encoder_settings.encode_dict_keys:  
                dct = dict()  
                if not self.encoder_settings.encode_dict_keys_postprocess:  
                    tmp = self.encoder_settings.encode_postprocessor  
                    self.encoder_settings.encode_postprocessor = lambda x: x  
                for (k, v) in var.iteritems():  
                    dct[self.encode(k)] = v  
                if not self.encoder_settings.encode_dict_keys_postprocess:  
                    self.encoder_settings.encode_postprocessor = tmp  
            else:  
                dct = dict(var)  
            if self.encoder_settings.encode_dict_vals:  
                if not self.encoder_settings.encode_dict_vals_postprocess:  
                    tmp = self.encoder_settings.encode_postprocessor  
                    self.encoder_settings.encode_postprocessor = lambda x: x  
                for (k, v) in dct.iteritems():  
                    dct[k] = self.encode(v)  
                if not self.encoder_settings.encode_dict_vals_postprocess:  
                    self.encoder_settings.encode_postprocessor = tmp  
            return dct  
        elif var is None:  
            if self.encoder_settings.encode_none:  
                return self.encoder_settings.encode_postprocessor(  
                    str(var).encode(self.encoder_settings.encode_to)  
                )  
            return None  
        raise TypeError('python built-in type expected, got \'%s\'', type(var))  
  
    def decode(self, var):  
        """  
        Decode strings in `self.decode_from` into python strings.  
  
        Compound types have their individual members decoded.  
  
        Dictionaries can have their values decoded into other types  
        by looking up keys in `self.decode_dict_vals_table`.  
  
        Returns a decoded copy of 'var'.  
        """  
        if isinstance(var, unicode):  
            return var  
        elif isinstance(var, str):  
            return self.encoder_settings.decode_postprocessor(  
                var.decode(self.encoder_settings.decode_from)  
            )  
        elif isinstance(var, (bool, float, Decimal, int, long)):  
            return var  
        elif isinstance(var, list):  
            return [self.decode(m) for m in var]  
        elif isinstance(var, tuple):  
            return tuple(self.decode(m) for m in var)  
        elif isinstance(var, dict):  
            if self.encoder_settings.decode_dict_keys:  
                dct = dict()  
                if not self.encoder_settings.decode_dict_keys_postprocess:  
                    tmp = self.encoder_settings.decode_postprocessor  
                    self.encoder_settings.decode_postprocessor = lambda x: x  
                for (k, v) in var.iteritems():  
                    dct[self.decode(k)] = v  
                if not self.encoder_settings.decode_dict_keys_postprocess:  
                    self.encoder_settings.decode_postprocessor = tmp  
            else:  
                dct = dict(var)  
            if self.encoder_settings.decode_dict_vals:  
                if not self.encoder_settings.decode_dict_vals_postprocess:  
                    tmp = self.encoder_settings.decode_postprocessor  
                    self.encoder_settings.decode_postprocessor = lambda x: x  
                for (k, v) in dct.iteritems():  
                    dct[k] = self._decode_dict_val(k, v)  
                if not self.encoder_settings.decode_dict_vals_postprocess:  
                    self.encoder_settings.decode_postprocessor = tmp  
            return dct  
        elif var is None:  
            if self.encoder_settings.decode_none:  
                return self.encoder_settings.decode_postprocessor(  
                    str(var).decode(self.encoder_settings.decode_from)  
                )  
            return None  
        raise TypeError('python built-in type expected, got \'%s\'', type(var))  
  
## ENCODER METHOD DECORATORS  
  
def encode_args(*outer_args):  
    def decorate(f):  
        def new_f(*args, **kwargs):  
            assert isinstance(args[0], Encoder), \  
                'first argument not Encoder instance'  
            new_args = list(args)  
            for a in outer_args:  
                if isinstance(a, int):  
                    if a < len(args):  
                        new_args[a] = args[0].encode(args[a])  
                elif isinstance(a, basestring):  
                    if a in kwargs:  
                        kwargs[a] = args[0].encode(kwargs[a])  
                else:  
                    raise TypeError(  
                        'encode_args takes a list of ints and basestrings'  
                    )  
            return f(*new_args, **kwargs)  
        new_f.func_name = f.func_name  
        return new_f  
    return decorate  
  
  
def decode_retval():  
    def decorate(f):  
        def new_f(*args, **kwargs):  
            assert isinstance(args[0], Encoder), \  
                'first argument not Encoder instance'  
            return args[0].decode(f(*args, **kwargs))  
        new_f.func_name = f.func_name  
        return new_f  
    return decorate  
  
                
             |