Drizzled Public API Documentation

packet.py
1 #!/usr/bin/env python
2 #
3 # Drizzle Client & Protocol Library
4 #
5 # Copyright (C) 2008 Eric Day (eday@oddments.org)
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions are
10 # met:
11 #
12 # * Redistributions of source code must retain the above copyright
13 # notice, this list of conditions and the following disclaimer.
14 #
15 # * Redistributions in binary form must reproduce the above
16 # copyright notice, this list of conditions and the following disclaimer
17 # in the documentation and/or other materials provided with the
18 # distribution.
19 #
20 # * The names of its contributors may not be used to endorse or
21 # promote products derived from this software without specific prior
22 # written permission.
23 #
24 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 #
36 
37 '''
38 MySQL Protocol Packet Objects
39 '''
40 
41 import struct
42 import unittest
43 
44 class PacketException(Exception):
45  pass
46 
47 class Packet(object):
48  '''This class represents a packet header.'''
49 
50  def __init__(self, packed=None, size=0, sequence=0):
51  if packed is None:
52  self.size = size
53  self.sequence = sequence
54  else:
55  data = struct.unpack('4B', packed)
56  self.size = data[0] | (data[1] << 8) | (data[2] << 16)
57  self.sequence = data[3]
58 
59  self.verify()
60 
61  def pack(self):
62  self.verify()
63  return struct.pack('4B',
64  self.size & 0xFF,
65  (self.size >> 8) & 0xFF,
66  (self.size >> 16) & 0xFF,
67  self.sequence % 256)
68 
69  def verify(self):
70  if self.size >= 16777216:
71  raise PacketException('Packet size cannot exceed 16777215 bytes (%d)' %
72  self.size)
73 
74  def __str__(self):
75  return '''Packet
76  size = %s
77  sequence = %s
78 ''' % (self.size, self.sequence)
79 
80 class TestPacket(unittest.TestCase):
81 
82  def testDefaultInit(self):
83  packet = Packet()
84  self.assertEqual(packet.size, 0)
85  self.assertEqual(packet.sequence, 0)
86  packet.__str__()
87 
88  def testKeywordInit(self):
89  packet = Packet(size=1234, sequence=5)
90  self.assertEqual(packet.size, 1234)
91  self.assertEqual(packet.sequence, 5)
92  packet.__str__()
93 
94  def testUnpackInit(self):
95  packet = Packet(struct.pack('4B', 210, 4, 0, 5))
96  self.assertEqual(packet.size, 1234)
97  self.assertEqual(packet.sequence, 5)
98 
99  def testPack(self):
100  packet = Packet(Packet(size=1234, sequence=5).pack())
101  self.assertEqual(packet.size, 1234)
102 
103  def testPackRange(self):
104  for x in range(0, 300):
105  packet = Packet(Packet(size=x, sequence=x).pack())
106  self.assertEqual(packet.size, x)
107  self.assertEqual(packet.sequence, x % 256)
108 
109  # 997 is a random prime number so we hit various increments
110  for x in range(300, 16777216, 997):
111  packet = Packet(Packet(size=x, sequence=x).pack())
112  self.assertEqual(packet.size, x)
113  self.assertEqual(packet.sequence, x % 256)
114 
115  packet = Packet(Packet(size=16777215).pack())
116  self.assertEqual(packet.size, 16777215)
117  self.assertEqual(packet.sequence, 0)
118 
119  self.assertRaises(PacketException, Packet, size=16777216)
120  self.assertRaises(PacketException, Packet, size=16777217)
121  self.assertRaises(PacketException, Packet, size=4294967295)
122  self.assertRaises(PacketException, Packet, size=4294967296)
123  self.assertRaises(PacketException, Packet, size=4294967297)
124 
125 def parse_row(count, data):
126  row = []
127  while count > 0:
128  count -= 1
129  if ord(data[0]) == 251:
130  row.append(None)
131  data = data[1:]
132  else:
133  (size, packed_size) = parse_encoded_size(data)
134  row.append(data[packed_size:packed_size+size])
135  data = data[packed_size+size:]
136  return row
137 
138 class BadSize(Exception):
139  pass
140 
141 def parse_encoded_size(data):
142  size = ord(data[0])
143  packed_size = 1
144  if size == 252:
145  size = struct.unpack('<H', data[1:3])[0]
146  packed_size = 3
147  elif size == 253:
148  data = struct.unpack('<HB', data[1:4])
149  size = data[0] | (data[1] << 16)
150  packed_size = 4
151  elif size == 254:
152  data = struct.unpack('<II', data[1:9])
153  size = data[0] | (data[1] << 32)
154  packed_size = 8
155  elif size == 255:
156  raise BadSize(str(size))
157 
158  return (size, packed_size)
159 
160 if __name__ == '__main__':
161  unittest.main()