Including binary data in Python scripts
Sometimes I need to use some binary data (e.g. a bitmap image) in a Python
script.
In a package, you can store this data portable in a data
subdirectory of
a modules using the package_data
argument of setuptools.setup
.
This won’t work in a standalone script.
You can of course make your script into a package, but here I want to show an
alternative solution.
Preparing the data
For example, let’s start with this image:
This is an 8-bit RGBA image. Since logo’s like this typically have few colors, we can make it smaller by converting it to a colormapped image.
convert python-powered-w-200x80.png PNG8:python-powered-w-200x80-cmap.png
This reduces the file size from 8758 bytes to 2483 bytes.
For non-image binary data, it might make sense to compress it with one of the compression formats that Python can use natively, like zlib, bz2 or lzma.
Converting to Python
Using the following code, we can convert the image or data file to Python code that can be included into our script.
import base64
import os
def to_include(path):
linelen = 74
with open(path, 'rb') as img:
data = base64.b85encode(img.read()).decode('ascii')
i = 0
print('# ' + path)
print('data = base64.b85decode(')
while i < len(data):
print(" '" + data[i:i+linelen] + "'")
i += linelen
print(")" + os.linesep)
So we run:
to_include('python-powered-w-200x80-cmap.png')
The result is:
# python-powered-w-200x80-cmap.png
data = base64.b85decode(
'iBL{Q4GJ0x0000DNk~Le0002U0000`2m=5B0Bf_#f&c&j1ZP1_K>z@;j|==^1poj5AY({UO#lF'
'TCIA3{ga82g0001h=l}q9FaQARU;qF*m;eA5aGbhPJOBUy_E1bzMgRZ*%*@QYySrv)W{8N0?(Y'
'A)%-xy0-H58pcdE>0nY&epyF_=YGk2<0h^l6YyLXwpcdEOYyUgzY|IFR)GiItYW|?M)s;ax)-R'
'|z0nVCd)ncdy)h`Y>pnau9)?%mzpcXxNHs;ax)?yAh)-R}QHh`U6Hs{jAY|J_yp-Ddypng8y)|'
'I9Q0|L*_HMF0QYGk3dYnauy)cmM8)|J{iH-9-QHs{ihHh`ZgYyUe@H?jlupcbTeYh?!N0nTVOY'
'Rd<>H?#zg)yP2xH%-!9)%*<7pyZ_8pGgWspRfu<pszhdpGiHcHcZkf)-I=P)|NpzdM6#a%0004'
'EOGiWihy@);00007bV*G`2jdGE4ImUb|L$P`00)&xL_t(&-tC)vdzwfTheuR2CU#r3aV4}MN!x'
'gLYufD6(MqG)6|vd<{$FP2%1uB9jZK~>zds_(g*orcIdkD50KDi$FM82&m6Lw81&6`N=L?VAL{'
'To4Pb#Np)#}-+-(J5d*W~&$1Zk8{t5k2#JvD!SW+3%S)zx`GuUgLuq;*z}135pJ4`aWg909~P9'
'@`)n#Y1r9^G5(_VEZc#AfHBooHw5hNC}yLhA&!?Am{3H0VyN%-;t|IB*+1#uH%5b-vRPw=M%JC'
'PV?RBSwQrh9`RTm2&l5dwJOQgrFI^x%`62*JMZQd@_A&6lxK25Bn4G!dwUhVK=KRv)?@Pm8yYf'
'cRW9pSuQGrb1fqajWj##|rfNrma2wsRAq5DF!G{ooD&(<k4Wv|H_7wzCP(wL<q2ZQeKtisCq~t'
'W+aP7tq(V6w6nBL}DMqVJM_5ty%#w59PAk2ddR#N9(fUih-$t~^{IvxmavYiTq_prvy<~=|O+C'
'dP&3LO!IcV}u4oGbYUgjUW4LE7m->cybS*+2lBV$y>sq=Mv+f&vMSj38lU1z`~?3ZnR|W;a40A'
'4=uQ=|@+eJe_TUTz81Nz5WgmB?Cyon{;`g34jz&sxgX+k^X?IJB$Y@P#gCGQRsS^8sw^4%?2_U'
'jSoE{`?%I+H8aSd6AO~h79)EEd*hEiKnA@S5RFcqI7tgzkp!ZL$OWd~&((cEMlm3K4bFS(TPhI'
'$f|%Q05EDQ?RQCcI_@^!(2;UzSf=eE;LU+?5Kyu!SSfETPLCS(4uR|b{APC!y7r0AF?q-l|&UI'
'c+iv#h{6?Tyl<f9-+4crXke2X4M3I^GO>#lvs5JRDxb)EZN+t6b`$o5x}48HkI36ktt5g-lF4S'
')>B0T6a5@|_zDcHC%71=k8AaIgx2xHZ^<k`Clb5aa@Y(I75Jh;^D|D3bs9qvZ@k!l&PYH-q6c{'
'G;oxUFh1arAvwwo#B-asVoR`6$+8T;7bT3UhVXU32}WE1F3-F*APfIHHg5eNL;6h9B)G$uK}2Z'
'Jj);L$(CyVqsGDYQ2^vj2H<uS1{tIU$@%w#2UpDg<n(>5R4Uc{YSaN3c1M{(v^@?*DHKKVDdwH'
'~`1b240>s<PCxJ+9p|?`PiWrd-lsxSBLb{m{icBDqQV@K*Ca&!mLBr8U2f(je8pVKk4~v^$$$I'
'#R@qx89Yn1`G>0|@}dbG8Yx4J!=!t2N>0DpFbKt}r;aR~ht0%;Jx5&`M%Z-jtMGbZC(kmC%1jv'
'z=+$dEnNgwvRuVi3fA*^?e*{4_v9(^d)G3W6LrL}u|I!B<+}=#(V@@<qgsz4aZ=<FXn7kV|2Z@'
'lipnc#xoP34?Uf8}Zm6mwSQyI5x;#eCEj>Ad~RJisoXvSbDee-Ta;%R85wwE$D|vDq9ldh3Wgo'
'MKz~QnxWQw4+8RhUQ?4nz~7lc{t3OsZZuaAi+|~1+T`|*cIKK|1R*ta4l&^!gs2Xmi)s!okGWd'
'p%3&zW4Jmf9fs3R8D`f-;kB7NhP30Dl9q5owpTbS8)mqqaM(o}Inh5E4AQu+7vs9q|q)BxP+H-'
'S>PZ8>7g$8Z`SL92J2OEB0g4VwXE)fKIz`M^Cti~2dryHY5?>hV#hc%<p_GD<Q-WV()e4vG(O*'
'{+_6iU>{H5r8|bol!cHhydR<G_nx3n>%^5!rz_u*S`B$Sw1jCb`{^TNO4T#C=|a4V+`KnsYil>'
'`5vu15CIgf1w;|K^){}9MmwO?uL0kc6=50Hf!F*x46GamP!F<_-@5|xJi7=1G%H423n-11!1_W'
'#6iL_GD)cGf{-pWslNc?Kqm1pY?Q;I#hwy79Sh4L2x}Ll32l;Yk<M^E%WL6yASSYx_hXC3jE$n'
'Ah<pffEfJPX*qUpqIq)2f_$EtVqj}4l)s6&W2SA8_`9_4;Y2t{-(K5xpBfCY|ILz49g?okNf!N'
'0ZS@C8TxX(?#V1Rpw0~;r3P3%=TS~l2H6_<+w@y3RZgB5NeGf1}&#*<wj5Z=>^HH1Yx4s&|TLb'
'r%K%wE%hFsVhWK=95W|I+Y|Ejm;d3w>V;nL)-lud@?`O;dCUw0K84Y-3@;4K>6@7e$)#GZN$h`'
'n$g7dZ63FZuKHTsQEnva+3%$t+N4H0&g?2;N6QnZbdH8QbHM3n~j>P5-j)IPvm{nc49d9EtxH<'
'Y4l$tNY@4FC)7ooqs?@&u=qw|fp#1ZvTGBby=V}akZ=A_XU}T)Q-=-V+I^M-H}m_2wIW@zw4r4'
'#YiNj<AgzW8u7%@0VL?POP@Cdiqr=@o{)yRU?#IH8Lp`Iuy5JjLlWq=44a;|s)tWfnZqhzjTDZ'
'_Gb%2m=j|A!54*U2GcGAhVhzGvDlmlPuJ@`LEcXI-XawGbA4cS+$z7+m;dXg~xCcebdf|Kd5SN'
'to}ZqiN*hrrVUA@8;#Ksx>8YARj#o|O8NEs{xpS0y#L;dmk%_In;ox7Q!;uF`mtAPndFdOS?QK'
'!lFjhyG8QU!~N`?u%aZqNC`49-Llwt;ZUX0000bbVXQnWMOn=I%9HWVRU5xGB7bYEio`HFg8>%'
'H99jmIxsXVFfckWFdV)gE&u=kC3HntbYx+4WjbwdWNBu305UK!GA%GSEiyM$GBG+ZHaapiD=;u'
'RFfhRbg9`uv02y>eSaefwW^{L9a%BK_cXuvnZfkR6VQ^(GZ*pgw?mQX*0000<MNUMnLSTX'
)
This fragment can be included in a Python script.
That script should also import base64
.
When the aforementioned script is run, data
references the content of
the original file.
In case this is compressed data you will have to add the decompression step yourself.
For comments, please send me an e-mail.
Related articles
- Profiling Python scripts(6): auto-orient
- Profiling with pyinstrument
- From python script to executable with cython
- On Python speed
- Python 3.11 speed comparison with 3.9