Package lepl :: Package bin :: Module encode
[hide private]
[frames] | no frames]

Source Code for Module lepl.bin.encode

  1   
  2  # The contents of this file are subject to the Mozilla Public License 
  3  # (MPL) Version 1.1 (the "License"); you may not use this file except 
  4  # in compliance with the License. You may obtain a copy of the License 
  5  # at http://www.mozilla.org/MPL/ 
  6  # 
  7  # Software distributed under the License is distributed on an "AS IS" 
  8  # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
  9  # the License for the specific language governing rights and 
 10  # limitations under the License. 
 11  # 
 12  # The Original Code is LEPL (http://www.acooke.org/lepl) 
 13  # The Initial Developer of the Original Code is Andrew Cooke. 
 14  # Portions created by the Initial Developer are Copyright (C) 2009-2010 
 15  # Andrew Cooke (andrew@acooke.org). All Rights Reserved. 
 16  # 
 17  # Alternatively, the contents of this file may be used under the terms 
 18  # of the LGPL license (the GNU Lesser General Public License, 
 19  # http://www.gnu.org/licenses/lgpl.html), in which case the provisions 
 20  # of the LGPL License are applicable instead of those above. 
 21  # 
 22  # If you wish to allow use of your version of this file only under the 
 23  # terms of the LGPL License and not to allow others to use your version 
 24  # of this file under the MPL, indicate your decision by deleting the 
 25  # provisions above and replace them with the notice and other provisions 
 26  # required by the LGPL License.  If you do not delete the provisions 
 27  # above, a recipient may use your version of this file under either the 
 28  # MPL or the LGPL License. 
 29   
 30  ''' 
 31  Convert structured Python data to a binary stream. 
 32   
 33  Writing a good API for binary encoding of arbitrary objects does not seem to 
 34  be easy.  In addition, this is my first attempt.  My apologies in advance. 
 35  This is a very basic library - the hope is that something like ASN.1 can 
 36  then be built on this (if someone buys me a copy of the spec...!) 
 37   
 38  The most obvious solution might be to require everything that must be encoded 
 39  implement some method.  Given Python's dynamic nature, ABCs, etc, this might 
 40  be possible, but it does seem that it could require some rather ugly hacks in 
 41  some cases, when using existing types. 
 42   
 43  The next simplest approach seems to be to use some kind of separate dispatch 
 44  (rather than the classes themselves) to convert things to a standard  
 45  intermediate fmt.  That is what I do here.  The intermediate fmt 
 46  is the pair (type, BitString), where "type" can be any value (but will be the 
 47  type of the value in all implementations here - value could be used, but we're 
 48  trying to give some impression of a layered approach). 
 49   
 50  Encoding a structure then requires three steps: 
 51   
 52  1. Defining a serialisation of composite structures.  Only acyclic structures 
 53     are considered (I am more interested in network protocols than pickling, 
 54     which already has a Python solution) 
 55       
 56  2. Converting individual values in the serial stream to the intermediate  
 57     representation. 
 58   
 59  3. Encoding the intermediate representation into a final BitString.    
 60   
 61  Support for each of these steps is provided by LEPL.  Stage 1 comes from the 
 62  graph and node modules; 2 is provided below (leveraging BitString's class  
 63  methods); 3 is only supported in a simple way below, with the expectation 
 64  that future modules might extend both encoding and matching to, for example,  
 65  ASN.1. 
 66  ''' 
 67   
 68  if bytes is str: 
 69      print('Binary parsing unsupported in this Python version') 
 70  else: 
 71   
 72      from functools import reduce as reduce_ 
 73      from operator import add 
 74       
 75      from lepl.bin.bits import BitString, STRICT 
 76      from lepl.support.graph import leaves 
 77      from lepl.support.node import Node 
 78       
 79       
80 - def dispatch_table(big_endian=True, encoding=None, errors=STRICT):
81 ''' 82 Convert types appropriately. 83 ''' 84 # pylint: disable-msg=W0108 85 # consistency 86 return {int: lambda n: BitString.from_int(n, ordered=big_endian), 87 str: lambda s: BitString.from_str(s, encoding, errors), 88 bytes: lambda b: BitString.from_bytearray(b), 89 bytearray: lambda b: BitString.from_bytearray(b), 90 BitString: lambda x: x}
91 92
93 - def make_converter(table):
94 ''' 95 Given a table, create the converter. 96 ''' 97 def converter(value): 98 ''' 99 The converter. 100 ''' 101 type_ = type(value) 102 if type_ in table: 103 return (type_, table[type_](value)) 104 for key in table: 105 if isinstance(value, key): 106 return (type_, table[key](value)) 107 raise TypeError('Cannot convert {0!r}:{1!r}'.format(value, type_))
108 return converter 109 110
111 - def simple_serialiser(node, table):
112 ''' 113 Serialize using the given table. 114 ''' 115 stream = leaves(node, Node) 116 converter = make_converter(table) 117 return reduce_(add, [converter(value)[1] for value in stream])
118