von Neumann
0x86
LDA #134
0x86 0x86
Code Data
0x86 0x86
lie
x86.js
compiler, n. data → code
secret:
code data
┌─────────────┐ ┌──────────┐
│ │ ← │ text │ ← 'file.py'
│ CPython │ → │ code obj │ → 'file.pyc'
│ │ ← │ bytecode │
└─────────────┘ └──────────┘
$ export PYTHONDONTWRITEBYTECODE=1
code data
┌─────────────┐ ┌──────────┐
│ │ ← │ text │ ← 'file.py'
│ CPython │ → │ code obj │
│ │ ← │ bytecode │
└─────────────┘ └──────────┘
>>> # Infinite paraboloid
>>>
>>> def f(x, y):
... return x * x + y * y
...
>>> f(3, 4)
25
>>> from dis import dis
>>> dis(f)
2 0 LOAD_FAST 0 (x)
3 LOAD_FAST 0 (x)
6 BINARY_MULTIPLY
7 LOAD_FAST 1 (y)
10 LOAD_FAST 1 (y)
13 BINARY_MULTIPLY
14 BINARY_ADD
15 RETURN_VALUE
see
ceval.c
for (;;) {
...
opcode = NEXTOP();
...
switch (opcode) {
TARGET(NOP) ...
TARGET(LOAD_FAST) ...
TARGET(BINARY_MULTIPLY) ...
PyNumber_Multiply(left, right); ...
TARGET(BINARY_ADD) ...
TARGET(RETURN_VALUE) ...
}
...
}
abstract.c
PyObject *
PyNumber_Multiply(PyObject *v, PyObject *w)
{
PyObject *result = binary_op1(
v, w, NB_SLOT(nb_multiply));
...
return result;
}
floatobject.c
static PyNumberMethods float_as_number = {
float_add, /*nb_add*/
float_sub, /*nb_subtract*/
float_mul, /*nb_multiply*/
float_rem, /*nb_remainder*/
float_divmod, /*nb_divmod*/
float_pow, /*nb_power*/
floatobject.c
static PyObject *
float_mul(PyObject *v, PyObject *w)
{
double a,b;
CONVERT_TO_DOUBLE(v, a);
CONVERT_TO_DOUBLE(w, b);
PyFPE_START_PROTECT("multiply", return 0)
a = a * b;
PyFPE_END_PROTECT(a)
return PyFloat_FromDouble(a);
}
code data
code data
─────────────────────── ─────────────
PyEval_EvalFrameEx() ↴
next bytecode
TARGET(BINARY_MULTIPLY) ↵
PyNumber_Multiply() ↴
<type 'float'>
float_mul() ↵
code data
─────────────────────── ─────────────
PyEval_EvalFrameEx() ↴
next bytecode
TARGET(BINARY_MULTIPLY) ↵
PyNumber_Multiply() ↴
<type 'float'>
float_mul() ↵
PyObject *res1, res2;
res1 = PyNumber_Multiply(x, x);
res2 = PyNumber_Multiply(y, y);
res3 = PyNumber_Add(res1, res2);
Py_DECREF(res1);
Py_DECREF(res2);
return res3;
40% speedup
float res1, res2;
res1 = x * x;
res2 = y * y;
return res1 + res2;
code data
─────────────────────── ─────────────
PyEval_EvalFrameEx() ↴
next bytecode
TARGET(BINARY_MULTIPLY) ↵
PyNumber_Multiply() ↴
<type 'float'>
float_mul() ↵
@numba.jit('f8,f8')
def f(x, y):
return x * x + y * y
see also MicroPython
@micropython.native
...
@micropython.viper
...
Old: | Reigning: | Announced: |
---|---|---|
Psyco | PyPy | Pyston |
Unladen Swallow | ||
(ShedSkin) |
Why?
python.exe
app/__init__.py
app/module_a.py
app/module_b.py
app/startup.py
otherlib/__init__.py
otherlib/module.py
binlib1.pyd
binlib2.pyd
dependency1.dll
dependency2.dll
start.bat
# python.exe -m app.startup
python.exe
source.zip
binlib1.pyd
binlib2.pyd
dependency1.dll
dependency2.dll
start.bat
# python.exe source.zip
$ (echo '#!/usr/bin/env python2';
cat source.zip) > app
$ chmod +x app
$ ./app
$ cat python.exe source.zip > app.exe
app.exe
binlib1.pyd
binlib2.pyd
dependency1.dll
dependency2.dll
start.bat
# app.exe app.exe
app.exe
binlib1.pyd
binlib2.pyd
dependency1.dll
dependency2.dll
sitecustomize.py
What is left?
app.exe
binlib1.pyd
binlib2.pyd
dependency1.dll
dependency2.dll
Binary modules and DLLs
DWORD *patchAddrHL;
int type, offset;
// the upper 4 bits define the type of relocation
type = *relInfo >> 12;
// the lower 12 bits define the offset
offset = *relInfo & 0xfff;
Thanks to this one weird trick
(Text, templates, images)
import pkgutil
textdata = pkgutil.get_data(
'app', 'templates/layout.html')
code data
─────────────────────── ─────────────
PyEval_EvalFrameEx() ↴
next bytecode
TARGET(BINARY_MULTIPLY) ↵
PyNumber_Multiply() ↴
<type 'float'>
float_mul() ↵
“This saved my life once”
“+1 another life saved ;)”
“+1 .. and another :)”
“+1 .. and me :P”
“just saved me from a nasty accident”
PYTHONDONTWRITEBYTECODE=1
git init
What if you need a real EXE?
res1 = PyNumber_Multiply(x, x);
res2 = PyNumber_Multiply(y, y);
res3 = PyNumber_Add(res1, res2);
Compiler + Bundler
Nuitka has a competitor
Compiler
cascade imports cascade2 import cascade3
cdef extern:
void initcascade()
void initcascade3()
void initcascade2()
initcascade3()
initcascade2()
initcascade()
import cascade
cascade.main()
Produces Python-version agnostic C
$ ~/venv-27/bin/cython boto/...
$ ...(compile)...
$ ~/venv-33/bin/python -c 'import boto'
ImportError: No module named StringIO
ImportError: No module named ConfigParser
etc
$ python test_boto.py
de405-1997.tar.gz
de406-1997.tar.gz
de421-2008.tar.gz
de422-2009.tar.gz
de423-2010.tar.gz
novas_de405-1997.tar.gz
packages.html
cdef float f(float x, float y):
return x * x + y * y
Cython → C → .so → PyInstaller → single file