Skip to content

Commit f77ffe3

Browse files
committed
Python 3.4 compatibility.
1 parent a4b1038 commit f77ffe3

File tree

4 files changed

+77
-90
lines changed

4 files changed

+77
-90
lines changed

setup.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@
33
# Setup definitions
44
setup(
55
name="python-tinylink",
6-
version="1.1",
6+
version="2.0.0",
77
description="Frame-based streaming protocol for embedded applications.",
88
author="Bas Stottelaar",
99
author_email="basstottelaar@gmail.com",
1010
packages=["tinylink"],
1111
license="MIT",
1212
keywords="python embedded arm arduino tinylink streaming serial",
13-
test_suite="tests",
13+
setup_requires=["nose"],
14+
install_requires=["six"],
15+
zip_safe=False,
1416
entry_points={
1517
"console_scripts": [
1618
"tinylink = tinylink.cli:run",
@@ -24,5 +26,6 @@
2426
"Topic :: Software Development :: Embedded Systems",
2527
"Programming Language :: Python :: 2",
2628
"Programming Language :: Python :: 2.7",
27-
],
29+
"Programming Language :: Python :: 3.4"
30+
]
2831
)

tinylink/__init__.py

Lines changed: 32 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
from tinylink import utils
22

33
import struct
4+
import six
45

56
__version__ = "1.1"
67

7-
__all__ = ["Frame", "DamagedFrame", "ResetFrame", "TinyLink"]
8+
__all__ = ["Frame", "TinyLink"]
89

910
# This can be anything, and is used to synchronize a frame
1011
PREAMBLE = 0xAA55AA55
@@ -13,14 +14,16 @@
1314
LITTLE_ENDIAN = "<"
1415
BIG_ENDIAN = ">"
1516

16-
# Protocol states
17+
# Protocol states.
1718
WAITING_FOR_PREAMBLE = 1
1819
WAITING_FOR_HEADER = 2
1920
WAITING_FOR_BODY = 3
2021

21-
# Message flags
22+
# Message flags (reserved).
2223
FLAG_NONE = 0x00
2324
FLAG_RESET = 0x01
25+
FLAG_ERROR = 0x02
26+
FLAG_PRIORITY = 0x04
2427

2528
# Don't change these values!
2629
LEN_PREAMBLE = 4
@@ -32,44 +35,25 @@
3235
LEN_BODY = LEN_CRC
3336

3437

35-
class BaseFrame(object):
38+
class Frame(object):
3639
"""
37-
Base frame.
40+
Represents a frame.
3841
"""
3942

40-
def __init__(self, data=None, flags=FLAG_NONE):
43+
def __init__(self, data=None, flags=FLAG_NONE, damaged=False):
44+
if data is not None:
45+
if not type(data) == six.binary_type:
46+
raise ValueError("Provided data must be encoded as bytes.")
47+
else:
48+
data = bytes()
49+
4150
self.data = data
4251
self.flags = flags
52+
self.damaged = damaged
4353

4454
def __repr__(self):
45-
return "%s(%s, flags=%d)" % (
46-
self.__class__.__name__, repr(self.data), self.flags)
47-
48-
49-
class Frame(BaseFrame):
50-
"""
51-
Represent normal frame.
52-
"""
53-
pass
54-
55-
56-
class DamagedFrame(BaseFrame):
57-
"""
58-
Represent damaged frame.
59-
"""
60-
pass
61-
62-
63-
class ResetFrame(BaseFrame):
64-
"""
65-
Represent reset frame.
66-
"""
67-
68-
def __init__(self):
69-
super(ResetFrame, self).__init__(None, FLAG_RESET)
70-
71-
def __repr__(self):
72-
return "ResetFrame()"
55+
return "%s(%s, flags=%d, damaged=%s)" % (
56+
self.__class__.__name__, repr(self.data), self.flags, self.damaged)
7357

7458

7559
class TinyLink(object):
@@ -78,7 +62,7 @@ class TinyLink(object):
7862
applications that only use RX/TX. Every message is encapsulated in a frame.
7963
A frame has a header checksum and a frame checksum, to detect errors as
8064
fast as possible (this can happen when you jump right into a stream of
81-
packets).
65+
packets, without being synchronized).
8266
8367
A typical frame has 13 bytes overhead, and can have a data payload up to
8468
65536 bytes.
@@ -108,16 +92,22 @@ def __init__(self, handle, endianness=LITTLE_ENDIAN,
10892
self.handle = handle
10993
self.endianness = endianness
11094
self.max_length = max_length
95+
self.ignore_damaged = ignore_damaged
11196

11297
# Set initial state
11398
self.state = WAITING_FOR_PREAMBLE
11499

115100
# Pre-allocate buffer that fits header + body. The premable will be
116101
# cleared when it is detected, so it doesn't need space.
117102
self.stream = bytearray(max_length + LEN_HEADER + LEN_BODY)
118-
self.buffer = buffer(self.stream)
119103
self.index = 0
120104

105+
# Python 2 does not allow unpack from bytearray, but Python 3.
106+
if six.PY3:
107+
self.buffer = self.stream
108+
else:
109+
self.buffer = buffer(self.stream)
110+
121111
def write_frame(self, frame):
122112
"""
123113
Write a frame via the handle.
@@ -148,13 +138,6 @@ def write_frame(self, frame):
148138
# Write to file
149139
return self.handle.write(result)
150140

151-
def reset(self):
152-
"""
153-
Shorthand for `write_frame(ResetFrame())'.
154-
"""
155-
156-
return self.write_frame(ResetFrame())
157-
158141
def write(self, data, flags=FLAG_NONE):
159142
"""
160143
Shorthand for `write_frame(Frame(data, flags=flags))'.
@@ -164,9 +147,8 @@ def write(self, data, flags=FLAG_NONE):
164147

165148
def read(self, limit=1):
166149
"""
167-
Read up to `limit' bytes from the handle and process this byte. Returns
168-
a list of received frames, if any. A reset frame is indicated by a
169-
`ResetFrame' instance.
150+
Read up to `limit' bytes from the handle and process it. Returns a list
151+
of received frames, if any.
170152
"""
171153

172154
# List of frames received
@@ -180,7 +162,7 @@ def read(self, limit=1):
180162
return []
181163

182164
# Append to stream
183-
self.stream[self.index] = char
165+
self.stream[self.index] = ord(char)
184166
self.index += 1
185167

186168
# Decide what to do
@@ -212,8 +194,8 @@ def read(self, limit=1):
212194
if length > 0:
213195
self.state = WAITING_FOR_BODY
214196
else:
215-
# Empty frame to indicate a reset frame
216-
frames.append(ResetFrame())
197+
# Frame without body.
198+
frames.append(Frame(flags=flags))
217199

218200
self.index = 0
219201
self.state = WAITING_FOR_PREAMBLE
@@ -237,7 +219,7 @@ def read(self, limit=1):
237219
if checksum_b == utils.checksum_frame(result, checksum_a):
238220
frames.append(Frame(result, flags=flags))
239221
elif not self.ignore_damaged:
240-
frames.append(DamagedFrame(result, flags=flags))
222+
frames.append(Frame(result, flags=flags, damaged=True))
241223

242224
# Reset to start state
243225
self.index = 0

tinylink/cli.py

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import sys
1+
from six.moves import xrange
2+
from six import StringIO
3+
24
import csv
5+
import six
6+
import sys
7+
import time
38
import select
49
import struct
510
import tinylink
611
import argparse
7-
import cStringIO
8-
import time
912

1013
try:
1114
import serial
@@ -44,27 +47,28 @@ def parse_arguments():
4447

4548
def dump(prefix, data):
4649
"""
47-
Dump data as two hex columns
50+
Dump data as two hex columns.
4851
"""
4952

5053
result = []
5154
length = len(data)
5255

5356
for i in xrange(0, length, 16):
54-
hexstr = bytestr = ""
57+
hexstr = ""
58+
bytestr = b""
5559

5660
for j in xrange(0, 16):
5761
if i + j < length:
58-
b = ord(data[i + j])
62+
b = six.indexbytes(data, i + j)
5963
hexstr += "%02x " % b
60-
bytestr += data[i + j] if 0x20 <= b < 0x7F else "."
64+
bytestr += six.int2byte(b) if 0x20 <= b < 0x7F else b"."
6165
else:
6266
hexstr += " "
6367

6468
if (j % 4) == 3:
6569
hexstr += " "
6670

67-
result.append(prefix + " " + hexstr + bytestr)
71+
result.append(prefix + " " + hexstr + bytestr.decode("ascii"))
6872

6973
# Return concatenated string
7074
return "\n".join(result)
@@ -82,11 +86,8 @@ def process_link(link):
8286
sys.stdout.write("### Type = %s\n" % frame.__class__.__name__)
8387
sys.stdout.write("### Flags = 0x%04x\n" % frame.flags)
8488

85-
if type(frame) != tinylink.ResetFrame:
86-
sys.stdout.write("### Lenght = %d\n" % len(frame.data))
87-
sys.stdout.write(dump("<<<", frame.data) + "\n\n")
88-
else:
89-
sys.stdout.write("\n")
89+
sys.stdout.write("### Length = %d\n" % len(frame.data))
90+
sys.stdout.write(dump("<<<", frame.data) + "\n\n")
9091

9192

9293
def process_stdin(link):
@@ -96,19 +97,19 @@ def process_stdin(link):
9697

9798
command = sys.stdin.readline()
9899

99-
# End of file
100+
# End of file.
100101
if len(command) == 0:
101102
return False
102103

103-
# Very simple command parser
104-
items = list(
105-
csv.reader(cStringIO.StringIO(command.strip()), delimiter=" "))
104+
# Abuse the CSV module as a command parser, because CSV-like arguments are
105+
# possible.
106+
items = list(csv.reader(StringIO(command.strip()), delimiter=" "))
106107

107108
if not items:
108109
return
109110

110-
# Initialize state and start parsing
111-
frame = tinylink.BaseFrame()
111+
# Initialize state and start parsing.
112+
frame = tinylink.Frame()
112113
repeat = 1
113114
pack = "B"
114115

@@ -129,35 +130,33 @@ def process_stdin(link):
129130
raise ValueError("Unkown option: %s" % k)
130131
else:
131132
try:
132-
# Assume it is a float
133+
# Assume it is a float.
133134
value = struct.pack(link.endianness + pack, float(item))
134135
except:
135136
try:
136-
# Assume it is an int
137+
# Assume it is an int.
137138
value = struct.pack(
138139
link.endianness + pack, int(item, 0))
139140
except ValueError:
140-
# Assume it is a string
141-
value = ""
142-
143-
for b in item:
144-
value += struct.pack(link.endianness + "B", ord(b))
141+
# Assume it is a byte string.
142+
item = item.encode("ascii")
143+
value = struct.pack(
144+
link.endianness + str(len(item)) + "s", item)
145145

146-
# Concat to frame
147-
frame.data = (frame.data or "") + value
146+
# Concat to frame.
147+
frame.data = (frame.data or bytes()) + value
148148
except Exception as e:
149149
sys.stdout.write("Parse exception: %s\n" % e)
150-
return
151150

152-
# Output the data
151+
# Output the data.
153152
for i in xrange(repeat):
154153
sys.stdout.write("### Flags = 0x%04x\n" % frame.flags)
155154

156155
if frame.data:
157-
sys.stdout.write("### Lenght = %d\n" % len(frame.data))
156+
sys.stdout.write("### Length = %d\n" % len(frame.data))
158157
sys.stdout.write(dump(">>>", frame.data) + "\n\n")
159158

160-
# Send frame
159+
# Send the frame.
161160
try:
162161
link.write_frame(frame)
163162
except ValueError as e:

tinylink/utils.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
CRC32_POLYNOMIAL = 0xEDB88320L
2-
CRC32_INITIAL = 0x00000000L
1+
from six.moves import xrange
2+
3+
CRC32_POLYNOMIAL = 0xEDB88320
4+
CRC32_INITIAL = 0x00000000
35

46

57
def crc32(buf):
@@ -10,10 +12,10 @@ def crc32(buf):
1012
result = CRC32_INITIAL
1113

1214
def crc32_value(c):
13-
ulTemp1 = (result >> 8) & 0x00FFFFFFl
15+
ulTemp1 = (result >> 8) & 0x00FFFFFF
1416
ulCRC = (result ^ c) & 0xff
1517

16-
for i in range(8):
18+
for i in xrange(8):
1719
if ulCRC & 0x01:
1820
ulCRC = (ulCRC >> 1) ^ CRC32_POLYNOMIAL
1921
else:
@@ -46,4 +48,5 @@ def checksum_frame(data, checksum_header):
4648
Calculate checksum of both the checksum header and the data.
4749
"""
4850

49-
return crc32(data + bytearray([checksum_header])) & 0xffffffff
51+
return crc32(
52+
memoryview(data).tobytes() + bytearray([checksum_header])) & 0xFFFFFFFF

0 commit comments

Comments
 (0)