Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions python-stdlib/functools/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# functools

In CPython, `@functools.lru_cache` would also record the *order* of the keyword
arguments as they are passed to the function to cache the output for. So, a
function called with `(a=1, b=2)` and called with `(b=2, a=1)` will take up two
entries in the LRU cache even though they are virtually the same function
invocation.

MicroPython, however, seems to pre-sort keyword arguments. This means the
keyword arguments order is lost, and therefore the behaviour of
`@functools.lru_cache` differs slightly between the two implementations.
32 changes: 32 additions & 0 deletions python-stdlib/functools/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,35 @@ def reduce(function, iterable, initializer=None):
for element in it:
value = function(value, element)
return value


def lru_cache(*args, maxsize=128, **kwargs):
def o(f):
if maxsize == 0:
return f
values = {}
order = []

def i(*inner_args, **inner_kwargs):
# dict isn't hashable and kwargs are pre-sorted by key.
key = (inner_args, tuple(inner_kwargs.items()))
if key in values:
output = values[key]
if maxsize:
order.remove(key)
else:
output = f(*inner_args, **inner_kwargs)
if maxsize and len(order) == maxsize:
del values[order.pop()]
values[key] = output
if maxsize:
order.insert(0, key)
return output

return i

return o(args[0]) if args else o


def cache(*args):
return lru_cache(*args, maxsize=None)
2 changes: 1 addition & 1 deletion python-stdlib/functools/manifest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
metadata(version="0.0.7")
metadata(version="0.1.0")

module("functools.py")
166 changes: 166 additions & 0 deletions python-stdlib/functools/test_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
from functools import lru_cache, cache


@lru_cache(maxsize=8)
def f8(i):
global count
count += 1
return i


count = 0
for i in range(8):
assert f8(i) == i
assert count == 8
for i in range(8):
assert f8(i) == i
assert count == 8

assert f8(100) == 100
assert count == 9
assert f8(5) == 5
assert count == 9
assert f8(0) == 0
assert count == 10


@lru_cache
def fdefault(i):
global count
count += 1
return i


count = 0
for i in range(150):
assert fdefault(i) == i
assert count == 150
for i in range(149, -1, -1):
assert fdefault(i) == i
assert count == 150 + (150 - 128)


@lru_cache(maxsize=0)
def f0(i):
global count
count += 1
return i


count = 0
for i in range(150):
assert f0(i) == i
assert count == 150
for i in range(150):
assert f0(i) == i
assert count == 300


@lru_cache(maxsize=None)
def fnone(i):
global count
count += 1
return i


count = 0
for i in range(150):
assert fnone(i) == i
assert count == 150
for i in range(150):
assert fnone(i) == i
assert count == 150


@lru_cache
def fretnone(i):
global count
count += 1


count = 0
for i in range(150):
assert fretnone(i) is None
assert count == 150
for i in range(149, -1, -1):
assert fretnone(i) is None
assert count == 150 + (150 - 128)


@lru_cache
def fargs(i, i2):
global count
count += 1
return i


count = 0
for i in range(150):
assert fargs(i, i) == i
assert count == 150
for i in range(149, -1, -1):
assert fargs(i, i) == i
assert count == 150 + (150 - 128)
for i in range(150):
assert fargs(i, 10000) == i
assert count == 150 + (150 - 128) + 150


@lru_cache
def fvarargs(i, *args):
global count
count += 1
return i


count = 0
for i in range(150):
if i & 1 == 0:
assert fvarargs(i) == i
else:
assert fvarargs(i, 'micropython') == i
assert count == 150
for i in range(149, -1, -1):
if i & 1 == 0:
assert fvarargs(i) == i
else:
assert fvarargs(i, 'micropython') == i
assert count == 150 + (150 - 128)
for i in range(150):
assert fvarargs(i, 'micropython', 'more micropython') == i
assert count == 150 + (150 - 128) + 150


@lru_cache
def fkwargs(i, i2=None, i3=0, i4="a"):
global count
count += 1
return i


count = 0
for i in range(150):
assert fkwargs(i, i3=i) == i
assert count == 150
for i in range(149, -1, -1):
assert fkwargs(i, i3=i) == i
assert count == 150 + (150 - 128)
for i in range(150):
assert fkwargs(i, i3=10000) == i
assert count == 150 + (150 - 128) + 150


@cache
def fcache(i):
global count
count += 1
return i


count = 0
for i in range(150):
assert fcache(i) == i
assert count == 150
for i in range(150):
assert fcache(i) == i
assert count == 150
1 change: 1 addition & 0 deletions tools/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ function ci_package_tests_run {
python-stdlib/collections-defaultdict/test_defaultdict.py \
python-stdlib/functools/test_partial.py \
python-stdlib/functools/test_reduce.py \
python-stdlib/functools/test_cache.py \
python-stdlib/heapq/test_heapq.py \
python-stdlib/hmac/test_hmac.py \
python-stdlib/itertools/test_itertools.py \
Expand Down
Loading