Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: opreab/micropython-lib
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: master
Choose a base ref
...
head repository: opreab/micropython-lib
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: opreab-logging
Choose a head ref
Able to merge. These branches can be automatically merged.

Commits on May 6, 2018

  1. Copy the full SHA
    6975d1e View commit details
  2. Copy the full SHA
    f6416a1 View commit details
  3. Copy the full SHA
    25e9f1b View commit details
  4. Copy the full SHA
    1a1518a View commit details

Commits on May 11, 2018

  1. Copy the full SHA
    ab3198e View commit details
  2. uasyncio.core: Switch to separate run and wait queues.

    Instead of using single priority queue for all tasks, split into using
    "run queue", which represents tasks not waiting until specific time,
    which should be run on every (well, next) loop iteration, and wait queue,
    still a priority queue. Run queue is a simple FIFO, implemented by
    ucollections.deque, recently introduced in pfalcon/micropython. Thus,
    there's minimal storage overhead and intrinsic scheduling fairness.
    Generally, run queue should hold both a callback/coro and its arguments,
    but as we don't feed any send args into coros still, it's optimized to
    hold just 1 items for coros, while 2 for callbacks.
    
    Introducing run queue will also allow to get rid of tie-breaking counter
    in utimeq implementation, which was introduced to enforce fair scheduling.
    It's no longer needed, as all tasks which should be run at given time
    are batch-removed from wait queue and batch-inserted into run queue. So,
    they may be executed not in the order scheduled (due to non-stable order
    of heap), but the whole batch will be executed "atomically", and any new
    schedulings from will be processed no earlier than next loop iteration.
    pfalcon authored and dpgeorge committed May 11, 2018
    Copy the full SHA
    4c63ecf View commit details
  3. Copy the full SHA
    7e8a3cf View commit details
  4. Copy the full SHA
    5149a54 View commit details
  5. uasyncio.core: Release 2.0.

    pfalcon authored and dpgeorge committed May 11, 2018
    Copy the full SHA
    97be334 View commit details
  6. uasyncio: Release 2.0.

    pfalcon authored and dpgeorge committed May 11, 2018
    Copy the full SHA
    4238bc9 View commit details
  7. Copy the full SHA
    8d75a36 View commit details
  8. uasyncio.udp: Release 0.1.1.

    pfalcon authored and dpgeorge committed May 11, 2018
    Copy the full SHA
    0a581ef View commit details
  9. Copy the full SHA
    912f941 View commit details
  10. unittest: Release 0.3.2.

    pfalcon authored and dpgeorge committed May 11, 2018
    Copy the full SHA
    220b501 View commit details
  11. Copy the full SHA
    f788f66 View commit details
  12. Copy the full SHA
    6b67e35 View commit details
  13. Copy the full SHA
    0feab33 View commit details
  14. logging: Release 0.2.

    pfalcon authored and dpgeorge committed May 11, 2018
    Copy the full SHA
    a93d0ee View commit details
  15. logging: Add setLevel() method.

    pfalcon authored and dpgeorge committed May 11, 2018
    Copy the full SHA
    09c59c4 View commit details
  16. logging: Add exc() and exception() methods.

    Non-standard exc() method accepts exception instance to log as a
    parameter. exception() just uses sys.exc_info().
    pfalcon authored and dpgeorge committed May 11, 2018
    Copy the full SHA
    b97fe09 View commit details
  17. Copy the full SHA
    a4f2d56 View commit details
  18. logging: Release 0.3.

    pfalcon authored and dpgeorge committed May 11, 2018
    Copy the full SHA
    f20d89c View commit details

Commits on May 30, 2018

  1. Copy the full SHA
    9961867 View commit details
40 changes: 40 additions & 0 deletions logging/example_logging.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,46 @@
import os
import time
import logging


def reset_log(log_file):
try:
os.remove(log_file)
except OSError:
pass


def stress_test(iterations):
sample = "All work and no play, makes Jack a dull boy!"
log_file = 'file.log'
reset_log(log_file)
logging.basicConfig(filename=log_file)
start_time = time.time()
for i in range(iterations):
logging.info("%d %s" % (i, sample))
file_delta = time.time() - start_time
reset_log(log_file)

logging.basicConfig(filename=None)
start_time = time.time()
for i in range(iterations):
logging.info("%d %s" % (i, sample))
stdout_delta = time.time() - start_time
print("File logging time %f for %i iterations" % (file_delta, iterations))
print("Stdout logging time %f for %i iterations" % (stdout_delta, iterations))


logging.basicConfig(level=logging.INFO)
log = logging.getLogger("test")
log.debug("Test message: %d(%s)", 100, "foobar")
log.info("Test message2: %d(%s)", 100, "foobar")

log.warning("Test message3: %d(%s)")
log.error("Test message4")
log.critical("Test message5")
logging.info("Test message6")

try:
1/0
except:
log.exception("Some trouble (%s)", "expected")
46 changes: 36 additions & 10 deletions logging/logging.py
Original file line number Diff line number Diff line change
@@ -19,18 +19,35 @@

class Logger:

level = NOTSET

def __init__(self, name):
self.level = NOTSET
self.name = name

def _level_str(self, level):
if level in _level_dict:
return _level_dict[level]
return "LVL" + str(level)
l = _level_dict.get(level)
if l is not None:
return l
return "LVL%s" % level

def setLevel(self, level):
self.level = level

def isEnabledFor(self, level):
return level >= (self.level or _level)

def log(self, level, msg, *args):
if level >= (self.level or _level):
print(("%s:%s:" + msg) % ((self._level_str(level), self.name) + args), file=_stream)
if _log_file:
_log_file.write(("%s:%s:" + msg + "\n") % ((self._level_str(level), self.name) + args))
_log_file.flush()
else:
_stream.write("%s:%s:" % (self._level_str(level), self.name))
if not args:
print(msg, file=_stream)
else:
print(msg % args, file=_stream)


def debug(self, msg, *args):
self.log(DEBUG, msg, *args)
@@ -47,9 +64,17 @@ def error(self, msg, *args):
def critical(self, msg, *args):
self.log(CRITICAL, msg, *args)

def exc(self, e, msg, *args):
self.log(ERROR, msg, *args)
sys.print_exception(e, _stream)

def exception(self, msg, *args):
self.exc(sys.exc_info()[1], msg, *args)


_level = INFO
_loggers = {}
_log_file = None

def getLogger(name):
if name in _loggers:
@@ -65,11 +90,12 @@ def debug(msg, *args):
getLogger(None).debug(msg, *args)

def basicConfig(level=INFO, filename=None, stream=None, format=None):
global _level, _stream
global _level, _stream, _log_file
_level = level
if stream:
_stream = stream
if filename is not None:
print("logging.basicConfig: filename arg is not supported")
if filename:
_log_file = open(filename, 'a')
else:
if stream:
_stream = stream
if format is not None:
print("logging.basicConfig: format arg is not supported")
2 changes: 1 addition & 1 deletion logging/metadata.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
srctype = micropython-lib
type = module
version = 0.1.3
version = 0.3
2 changes: 1 addition & 1 deletion logging/setup.py
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
import sdist_upip

setup(name='micropython-logging',
version='0.1.3',
version='0.3',
description='logging module for MicroPython',
long_description="This is a module reimplemented specifically for MicroPython standard library,\nwith efficient and lean design in mind. Note that this module is likely work\nin progress and likely supports just a subset of CPython's corresponding\nmodule. Please help with the development if you are interested in this\nmodule.",
url='https://github.com/micropython/micropython-lib',
37 changes: 37 additions & 0 deletions logging/test_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import logging
import os


def reset_log(log_file):
try:
os.remove(log_file)
except OSError:
pass


def test_basicConfig():
print("Testing logging.basicConfig")
log_file = 'file.log'
log_message = '123'

reset_log(log_file)
logging.basicConfig(filename=log_file)
logging.info(log_message)

with open(log_file, 'r') as logf:
assert log_message in logf.read()

print("Success: Testing logging.basicConfig")


tests = [test_basicConfig]


def test():
print("Running %i tests." % len(tests))
for test_function in tests:
test_function()


if __name__ == '__main__':
test()
2 changes: 1 addition & 1 deletion uasyncio.core/metadata.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
srctype = micropython-lib
type = package
version = 1.7.2
version = 2.0
author = Paul Sokolovsky
desc = Lightweight asyncio-like library for MicroPython, built around native Python coroutines. (Core event loop).
long_desc = Lightweight asyncio-like library for MicroPython, built around native Python coroutines. (Core event loop).
2 changes: 1 addition & 1 deletion uasyncio.core/setup.py
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
import sdist_upip

setup(name='micropython-uasyncio.core',
version='1.7.2',
version='2.0',
description='Lightweight asyncio-like library for MicroPython, built around native Python coroutines. (Core event loop).',
long_description='Lightweight asyncio-like library for MicroPython, built around native Python coroutines. (Core event loop).',
url='https://github.com/micropython/micropython-lib',
2 changes: 1 addition & 1 deletion uasyncio.core/test_full_wait.py
Original file line number Diff line number Diff line change
@@ -47,4 +47,4 @@ def cb_2nd():
print(loop.msgs)
# .wait() is now called on each loop iteration, and for our mock case, it means that
# at the time of running, self.time() will be skewed by 100 virtual time units.
assert loop.msgs == ['I should be run first, time: 200', 'I should be run second, time: 600'], str(loop.msgs)
assert loop.msgs == ['I should be run first, time: 100', 'I should be run second, time: 500'], str(loop.msgs)
101 changes: 62 additions & 39 deletions uasyncio.core/uasyncio/core.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import utime as time
import utimeq
import ucollections


type_gen = type((lambda: (yield))())
@@ -25,8 +26,9 @@ class TimeoutError(CancelledError):

class EventLoop:

def __init__(self, len=42):
self.q = utimeq.utimeq(len)
def __init__(self, runq_len=16, waitq_len=16):
self.runq = ucollections.deque((), runq_len, True)
self.waitq = utimeq.utimeq(waitq_len)
# Current task being run. Task is a top-level coroutine scheduled
# in the event loop (sub-coroutines executed transparently by
# yield from/await, event loop "doesn't see" them).
@@ -41,18 +43,24 @@ def create_task(self, coro):
# CPython asyncio incompatibility: we don't return Task object

def call_soon(self, callback, *args):
self.call_at_(self.time(), callback, args)
if __debug__ and DEBUG:
log.debug("Scheduling in runq: %s", (callback, args))
self.runq.append(callback)
if not isinstance(callback, type_gen):
self.runq.append(args)

def call_later(self, delay, callback, *args):
self.call_at_(time.ticks_add(self.time(), int(delay * 1000)), callback, args)

def call_later_ms(self, delay, callback, *args):
if not delay:
return self.call_soon(callback, *args)
self.call_at_(time.ticks_add(self.time(), delay), callback, args)

def call_at_(self, time, callback, args=()):
if __debug__ and DEBUG:
log.debug("Scheduling %s", (time, callback, args))
self.q.push(time, callback, args)
log.debug("Scheduling in waitq: %s", (time, callback, args))
self.waitq.push(time, callback, args)

def wait(self, delay):
# Default wait implementation, to be overriden in subclasses
@@ -64,45 +72,45 @@ def wait(self, delay):
def run_forever(self):
cur_task = [0, 0, 0]
while True:
if self.q:
# wait() may finish prematurely due to I/O completion,
# and schedule new, earlier than before tasks to run.
while 1:
t = self.q.peektime()
tnow = self.time()
delay = time.ticks_diff(t, tnow)
if delay < 0:
delay = 0
# Always call wait(), to give a chance to I/O scheduling
self.wait(delay)
if delay == 0:
break

self.q.pop(cur_task)
t = cur_task[0]
cb = cur_task[1]
args = cur_task[2]
# Expire entries in waitq and move them to runq
tnow = self.time()
while self.waitq:
t = self.waitq.peektime()
delay = time.ticks_diff(t, tnow)
if delay > 0:
break
self.waitq.pop(cur_task)
if __debug__ and DEBUG:
log.debug("Moving from waitq to runq: %s", cur_task[1])
self.call_soon(cur_task[1], *cur_task[2])

# Process runq
l = len(self.runq)
if __debug__ and DEBUG:
log.debug("Entries in runq: %d", l)
while l:
cb = self.runq.popleft()
l -= 1
args = ()
if not isinstance(cb, type_gen):
args = self.runq.popleft()
l -= 1
if __debug__ and DEBUG:
log.info("Next callback to run: %s", (cb, args))
cb(*args)
continue

if __debug__ and DEBUG:
log.debug("Next coroutine to run: %s", (t, cb, args))
log.info("Next coroutine to run: %s", (cb, args))
self.cur_task = cb
# __main__.mem_info()
else:
self.wait(-1)
# Assuming IO completion scheduled some tasks
continue
if callable(cb):
cb(*args)
else:
delay = 0
try:
if __debug__ and DEBUG:
log.debug("Coroutine %s send args: %s", cb, args)
if args == ():
if args is ():
ret = next(cb)
else:
ret = cb.send(*args)
if __debug__ and DEBUG:
log.debug("Coroutine %s yield result: %s", cb, ret)
log.info("Coroutine %s yield result: %s", cb, ret)
if isinstance(ret, SysCall1):
arg = ret.arg
if isinstance(ret, SleepMs):
@@ -147,7 +155,22 @@ def run_forever(self):
# Currently all syscalls don't return anything, so we don't
# need to feed anything to the next invocation of coroutine.
# If that changes, need to pass that value below.
self.call_later_ms(delay, cb)
if delay:
self.call_later_ms(delay, cb)
else:
self.call_soon(cb)

# Wait until next waitq task or I/O availability
delay = 0
if not self.runq:
delay = -1
if self.waitq:
tnow = self.time()
t = self.waitq.peektime()
delay = time.ticks_diff(t, tnow)
if delay < 0:
delay = 0
self.wait(delay)

def run_until_complete(self, coro):
def _run_and_stop():
@@ -195,10 +218,10 @@ class IOWriteDone(SysCall1):

_event_loop = None
_event_loop_class = EventLoop
def get_event_loop(len=42):
def get_event_loop(runq_len=16, waitq_len=16):
global _event_loop
if _event_loop is None:
_event_loop = _event_loop_class(len)
_event_loop = _event_loop_class(runq_len, waitq_len)
return _event_loop

def sleep(secs):
2 changes: 1 addition & 1 deletion uasyncio.udp/metadata.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
srctype = micropython-lib
type = package
version = 0.1
version = 0.1.1
author = Paul Sokolovsky
desc = UDP support for MicroPython's uasyncio
depends = uasyncio
2 changes: 1 addition & 1 deletion uasyncio.udp/setup.py
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
import sdist_upip

setup(name='micropython-uasyncio.udp',
version='0.1',
version='0.1.1',
description="UDP support for MicroPython's uasyncio",
long_description="This is a module reimplemented specifically for MicroPython standard library,\nwith efficient and lean design in mind. Note that this module is likely work\nin progress and likely supports just a subset of CPython's corresponding\nmodule. Please help with the development if you are interested in this\nmodule.",
url='https://github.com/micropython/micropython-lib',
2 changes: 1 addition & 1 deletion uasyncio.udp/uasyncio/udp.py
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@ def recvfrom(s, n):

def sendto(s, buf, addr=None):
while 1:
res = s.sendto(buf, 0, addr)
res = s.sendto(buf, addr)
#print("send res:", res)
if res == len(buf):
return
2 changes: 1 addition & 1 deletion uasyncio/benchmark/boom_uasyncio.py
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ def validate(resp):
no = int(l.split()[1])
seen.append(no)
c = t.count(l + "\r\n")
assert c == 400101
assert c == 400101, str(c)
assert t.endswith("=== END ===")

cnt += 1
Loading