OK we've done the hard part, we've extracted all the data from a TV-B-Gone, captured, parsed and compressed it. Now we've got a 45KB file of TV power codes in the form of python 'dictionaries'.

This is where things get a little interesting, and we get to take advantage of CircuitPython being an interpreted language.

If you've written this kind of code before in C or C++, you'd think "OK lets compile the codes in as raw binary data or as structures, and then refer to them in the compiled code".

With CircuitPython, you might thus think "OK lets just paste the codes.txt file into main.py and then iterate over the list" But if you tried that you'd quickly realize that with codes.txt being 45KB and the ATSAMD21 only has 32KB of RAM, so its impossible to load all this data into RAM.

Disk Storage

The next possible thought you might have is "ok well if I cant fit it all into RAM, lets put it on disk and parse the codes, we just have to write a parser" After all, that's how you'd do it with an Arduino C/C++

But, in python, we already have that parser! It's called eval() and its the engine of how Python and CircuitPython works.

Let's look at eval for a moment, so you can see how handy it is!

Evaluating eval()

Open up your REPL to CircuitPython (we're using Mu) and type in the following:

Download: file

You'll get this as a reply:

2, is the evaluated reply to 1+1

If you think about it, the text you wrote "1+1" is basically a command - saying "Hey CircuitPython parser, please add two numbers, as I've given them to you". The "1+1" command is not compiled into code, there's no lookup table where "1+1" is an entry and "2" is the output. Instead, the CircuitPython interpreter must read that text, parse the text, realize that its valid Python, and then evaluate it, before printing the answer.

We can be somewhat flexible in how we give it the text. For example, its valid Python to have a bunch of space between the + sign:

But we cannot have spaces before the first digit:

Given that Python is interpreted, there is a function that takes the text you've typed in either here at the prompt, or in main.py and runs each line. That function is called eval, and you can run it by hand like so:

Download: file

Same as before, we get 2 back.

Since you're just passing in a character string, you can get quite creative. For example, you can create dynamic code like:

Download: file
import random

maths = ["+", "-", "*", "/"]
eval("1" + maths[random.randint(0,3)] + "2")

This code creates a list with the 4 standard math operations, then evaluates a 'random' math operation between the integers 1 and 2 each time its run - creating a different string to evaluate each time:

Which, again, if you have only ever written compiled code, is very unusual.

But, since its built into Python, we can take advantage of it to do parsing for us - we simply have CircuitPython read each line of code.txt which contains the printed out version of the dictionary item, and parse it directly as an evaluated string. Taa-dah, we've converted data on disk to code to data in memory with one function call!

This guide was first published on Mar 18, 2018. It was last updated on Mar 18, 2018.

This page (The Magic of eval()) was last updated on Sep 29, 2020.