Browse Source

Python 3 compatibility

demetri 3 years ago
parent
commit
9fc617723b
7 changed files with 65 additions and 29 deletions
  1. 4 2
      nodemcu_uploader/main.py
  2. 19 25
      nodemcu_uploader/uploader.py
  3. 34 0
      nodemcu_uploader/utils.py
  4. 1 0
      test_requirements.txt
  5. 1 1
      tests/__init__.py
  6. 0 1
      tests/uploader.py
  7. 6 0
      tox.ini

+ 4 - 2
nodemcu_uploader/main.py

@@ -3,6 +3,7 @@
 
 """This module is the cli for the Uploader class"""
 
+from __future__ import print_function
 
 import argparse
 import logging
@@ -12,6 +13,7 @@ from .uploader import Uploader
 from .term import terminal
 from serial import VERSION as serialversion
 
+
 log = logging.getLogger(__name__) # pylint: disable=C0103
 from .version import __version__
 
@@ -73,8 +75,8 @@ def operation_upload(uploader, sources, verify, do_compile, do_file, do_restart)
 def operation_download(uploader, sources):
     """The download operation"""
     sources, destinations = destination_from_source(sources, False)
-    print 'sources', sources
-    print 'destinations', destinations
+    print('sources', sources)
+    print('destinations', destinations)
     if len(destinations) == len(sources):
         if uploader.prepare():
             for filename, dst in zip(sources, destinations):

+ 19 - 25
nodemcu_uploader/uploader.py

@@ -2,6 +2,10 @@
 # Copyright (C) 2015-2016 Peter Magnusson <peter@birchroad.net>
 """Main functionality for nodemcu-uploader"""
 
+# Not sure about it, because UnicodeEncodeError throws anyway
+from __future__ import unicode_literals
+
+
 import time
 import logging
 import hashlib
@@ -11,7 +15,7 @@ import serial
 
 from .exceptions import CommunicationTimeout, DeviceNotFoundException, \
     BadResponseException, VerificationError, NoAckException
-from .utils import default_port, system
+from .utils import default_port, system, wrap, hexify, from_file, ENCODING
 from .luacode import RECV_LUA, SEND_LUA, LUA_FUNCTIONS, \
     LIST_FILES, UART_SETUP, PRINT_FILE
 
@@ -46,6 +50,9 @@ class Uploader(object):
         else:
             self._port = serial.Serial(port, start_baud, timeout=timeout)
 
+        # black magic aka proxifying
+        self._port = wrap(self._port)
+
         self.start_baud = start_baud
         self.baud = baud
         # Keeps things working, if following conections are made:
@@ -143,7 +150,7 @@ class Uploader(object):
         if not binary:
             log.debug('write: %s', output)
         else:
-            log.debug('write binary: %s', ':'.join(x.encode('hex') for x in output))
+            log.debug('write binary: %s', hexify(output))
         self._port.write(output)
         self._port.flush()
 
@@ -260,9 +267,7 @@ class Uploader(object):
             log.error('did not ack destination filename')
             raise NoAckException('Device did not ACK destination filename')
 
-        fil = open(path, 'rb')
-        content = fil.read()
-        fil.close()
+        content = from_file(path)
 
         log.debug('sending %d bytes in %s', len(content), filename)
         pos = 0
@@ -275,7 +280,7 @@ class Uploader(object):
             data = content[pos:pos+rest]
             if not self.__write_chunk(data):
                 resp = self.__expect()
-                log.error('Bad chunk response "%s" %s', resp, ':'.join(x.encode('hex') for x in resp))
+                log.error('Bad chunk response "%s" %s', resp, hexify(resp))
                 raise BadResponseException('Bad chunk response', ACK, resp)
 
             pos += chunk_size
@@ -290,9 +295,7 @@ class Uploader(object):
         """Tries to verify if path has same checksum as destination.
             Valid options for verify is 'raw', 'sha1' or 'none'
         """
-        fil = open(path, 'rb')
-        content = fil.read()
-        fil.close()
+        content = from_file(path)
         log.info('Verifying using %s...' % verify)
         if verify == 'raw':
 
@@ -308,7 +311,7 @@ class Uploader(object):
             log.info('Remote SHA1: %s', data)
 
             #Calculate hash of local data
-            filehashhex = hashlib.sha1(content.encode()).hexdigest()
+            filehashhex = hashlib.sha1(content.encode(ENCODING)).hexdigest()
             log.info('Local SHA1: %s', filehashhex)
             if data != filehashhex:
                 log.error('SHA1 verification failed.')
@@ -319,17 +322,15 @@ class Uploader(object):
         elif verify != 'none':
             raise Exception(verify + ' is not a valid verification method.')
 
-
-
     def exec_file(self, path):
         """execute the lines in the local file 'path'"""
         filename = os.path.basename(path)
         log.info('Execute %s', filename)
 
-        fil = open(path, 'r')
+        content = from_file(path)
 
         res = '> '
-        for line in fil:
+        for line in content:
             line = line.rstrip('\r\n')
             retlines = (res + self.__exchange(line)).splitlines()
             # Log all but the last line
@@ -338,15 +339,13 @@ class Uploader(object):
                 log.info(lin)
         # last line
         log.info(res)
-        fil.close()
 
     def __got_ack(self):
         """Returns true if ACK is received"""
         log.debug('waiting for ack')
         res = self._port.read(1)
-        log.debug('ack read %s', res.encode('hex'))
-        return res == '\x06' #ACK
-
+        log.debug('ack read %s', hexify(res))
+        return res == ACK
 
     def write_lines(self, data):
         """write lines, one by one, separated by \n to device"""
@@ -354,9 +353,6 @@ class Uploader(object):
         for line in lines:
             self.__exchange(line)
 
-        return
-
-
     def __write_chunk(self, chunk):
         """formats and sends a chunk of data to the device according
         to transfer protocol"""
@@ -371,7 +367,6 @@ class Uploader(object):
         self._port.flush()
         return self.__got_ack()
 
-
     def __read_chunk(self, buf):
         """Read a chunk of data"""
         log.debug('reading chunk')
@@ -387,7 +382,7 @@ class Uploader(object):
             buf = buf + self._port.read()
 
         if buf[0] != BLOCK_START or len(buf) < 130:
-            log.debug('buffer binary: %s ', ':'.join(x.encode('hex') for x in buf))
+            log.debug('buffer binary: %s ', hexify(buf))
             raise Exception('Bad blocksize or start byte')
 
         if SYSTEM != 'Windows':
@@ -398,14 +393,13 @@ class Uploader(object):
         buf = buf[130:]
         return (data, buf)
 
-
     def file_list(self):
         """list files on the device"""
         log.info('Listing files')
         res = self.__exchange(LIST_FILES)
         log.info(res)
         res = res.split('\r\n')
-        #skip first and last lines
+        # skip first and last lines
         res = res[1:-1]
         files = []
         for line in res:

+ 34 - 0
nodemcu_uploader/utils.py

@@ -4,9 +4,14 @@
 
 from platform import system
 from os import environ
+from wrapt import ObjectProxy
 
 __all__ = ['default_port', 'system']
 
+
+ENCODING = 'latin1'
+
+
 def default_port(sysname=system()):
     """This returns the default port used for different systems if SERIALPORT env variable is not set"""
     system_default = {
@@ -15,3 +20,32 @@ def default_port(sysname=system()):
     }.get(sysname, '/dev/ttyUSB0')
     return environ.get('SERIALPORT', system_default)
 
+
+def bytefy(x):
+    return x if type(x) == bytes else x.encode(ENCODING)
+
+
+def to_hex(x):
+    return hex(x) if type(x) == bytes else hex(ord(x))
+
+
+def hexify(byte_arr):
+    return ':'.join((to_hex(x)[2:] for x in byte_arr))
+
+
+def from_file(path):
+    with open(path, 'rb') as f:
+        content = f.read().decode(ENCODING)
+    return content
+
+
+class DecoderWrapper(ObjectProxy):
+    def read(self, *args, **kwargs):
+        return self.__wrapped__.read(*args, **kwargs).decode(ENCODING)
+
+    def write(self, data):
+        return self.__wrapped__.write(data.encode(ENCODING))
+
+
+def wrap(x):
+    return DecoderWrapper(x)

+ 1 - 0
test_requirements.txt

@@ -1,2 +1,3 @@
 pyserial==3.0.1
 coverage==4.0.3
+wrapt==1.10.10

+ 1 - 1
tests/__init__.py

@@ -12,7 +12,7 @@ def get_tests():
 def full_suite():
     """creates a full suite of tests"""
     logging.basicConfig(filename='test.log', level=logging.INFO,
-        ormat='%(asctime)s %(levelname)s %(module)s.%(funcName)s %(message)s')
+        format='%(asctime)s %(levelname)s %(module)s.%(funcName)s %(message)s')
 
     from .misc import MiscTestCase
     from . import uploader

+ 0 - 1
tests/uploader.py

@@ -40,7 +40,6 @@ class UploaderTestCase(unittest.TestCase):
         self.uploader.prepare()
         self.uploader.write_file('tests/fixtures/big_file.txt', verify='raw')
 
-
     def test_upload_and_verify_sha1(self):
         self.uploader.prepare()
         self.uploader.write_file('tests/fixtures/big_file.txt', verify='sha1')

+ 6 - 0
tox.ini

@@ -0,0 +1,6 @@
+[tox]
+envlist = py27, py36
+
+[testenv]
+deps = -rtest_requirements.txt
+commands = python -m unittest -v tests.get_tests