Browse Source

Python 3 compatibility

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

+ 34 - 0
nodemcu_uploader/utils.py

@@ -4,9 +4,14 @@
 
 
 from platform import system
 from platform import system
 from os import environ
 from os import environ
+from wrapt import ObjectProxy
 
 
 __all__ = ['default_port', 'system']
 __all__ = ['default_port', 'system']
 
 
+
+ENCODING = 'latin1'
+
+
 def default_port(sysname=system()):
 def default_port(sysname=system()):
     """This returns the default port used for different systems if SERIALPORT env variable is not set"""
     """This returns the default port used for different systems if SERIALPORT env variable is not set"""
     system_default = {
     system_default = {
@@ -15,3 +20,32 @@ def default_port(sysname=system()):
     }.get(sysname, '/dev/ttyUSB0')
     }.get(sysname, '/dev/ttyUSB0')
     return environ.get('SERIALPORT', system_default)
     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
 pyserial==3.0.1
 coverage==4.0.3
 coverage==4.0.3
+wrapt==1.10.10

+ 1 - 1
tests/__init__.py

@@ -12,7 +12,7 @@ def get_tests():
 def full_suite():
 def full_suite():
     """creates a full suite of tests"""
     """creates a full suite of tests"""
     logging.basicConfig(filename='test.log', level=logging.INFO,
     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 .misc import MiscTestCase
     from . import uploader
     from . import uploader

+ 0 - 1
tests/uploader.py

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