Muitas pessoas querem, por diversos motivos, ocultar o código fonte dos seus programas. Muitas acham que compilando o mesmo para bytecode ou binário (nome dado ao bytecode da máquina hospedeira) vão conseguir fazê-lo. Vale lembrar, porém, que existem disassemblers que conseguem converter para instruções básicas legíveis pelo usuário final e até mesmo alguns que conseguem converter para estruturas de mais alto nível, ficando razoavelmente legível. Tais disassemblers existem para várias linguagens/bytecodes e o Python não é diferente.
O Python utiliza um bytecode para sua própria Virtual Machine (VM), ao rodar um código legível (.py) ele converte automaticamente para o bytecode e executa-o, sendo que por motivos de desempenho as bibliotecas/módulos são salvos como bytecode para um arquivo, em geral com extensão .pyc ou .pyo (otimizado, sem doc-strings). Pode-se, porém, distribuir somente o bytecode e tudo funcionará perfeitamente.
Deve ser lembrado que não só é possível como também é fácil transformar o bytecode do Python de volta para um arquivo legível a humanos. Veja http://packages.qa.debian.org/d/decompyle.html .
Como gerar bytecode para o arquivo principal
Ao executarmos um programa python, apenas os módulos importados serão salvos em bytecode, o arquivo principal, o qual foi chamado na linha de comando não. Para convertermos, podemos usar dois métodos:
Trivial
A maneira trivial é fazer import do seu módulo, para um arquivo chamado meucodigo.py:
python -OO -c "import meucodigo"
Esse método tem como efeito colateral que meucodigo é executado e não apenas compilado.
Usando py_compile
O Python provê um módulo para compilar arquivos, é o py_compile http://www.python.org/doc/lib/module-pycompile.html.
Criando um compilador
Use o programa pycompiler.py:
1 """
2 PyCompiler: compile python files to bytecode.
3 Author: Gustavo Sverzut Barbieri <barbieri@gmail.com>
4 License: GPL
5 """
6
7 import sys
8 import os
9 import getopt
10 import py_compile
11 import compileall
12
13 def usage():
14 print >> sys.stderr, """\
15 Usage:
16
17 %s [options] <file1> ... <fileN>
18
19 where options are:
20 -h, --help This message.
21 -r, --recursive Enter directories recursively.
22 -d, --maxdepth=N Maximum depth in recursion.
23 -f, --force Force recompiling already compiled files.
24 -q, --quiet Be quiet.
25
26 """ % sys.argv[ 0 ]
27
28
29 def compile_file( filename, force=False, quiet=False ):
30 if not quiet:
31 print "Compiling %s ..." % filename
32 py_compile.compile( filename )
33
34 def compile_dir( dirname, depth=1, force=False, quiet=False ):
35 compileall.compile_dir( dirname, depth, force=force, quiet=quiet )
36
37 def compile( name, depth=1, force=False, quiet=False ):
38 if os.path.isdir( name ):
39 compile_dir( name, depth, force, quiet )
40 elif os.path.exists( name ):
41 compile_file( name, force, quiet )
42 else:
43 if not quiet:
44 print >> sys.stderr, "File '%s' doesn't exists!" % name
45
46
47
48 if __name__ == "__main__":
49 try:
50 opts, args = getopt.getopt( sys.argv[ 1: ],
51 "hrd:qf",
52 [ "help",
53 "recursive",
54 "maxdepth=",
55 "quiet",
56 "force" ] )
57 except getopt.GetoptError:
58 usage()
59 sys.exit( 2 )
60
61
62 depth = 0
63 quiet = False
64 force = True
65
66 for o, a in opts:
67 if o in ( "-h", "--help" ):
68 usage()
69 sys.exit( 0 )
70 elif o in ( "-r", "--recursive" ):
71 if not depth:
72 depth = 20
73 elif o in ( "-d", "--maxdepth" ):
74 depth = int( a )
75 elif o in ( "-q", "--quiet" ):
76 quiet = True
77 elif o in ( "-f", "--force" ):
78 force = True
79
80
81 for name in args:
82 compile( name, depth, force, quiet )
Linha de comando
Caso não queira criar um script para compilar os seus programas, pode usar:
python -OO -c "import py_compile; py_compile.main()" meucodigo1.py meucodigo2.py
Outras Técnicas
Outra técnica para ocultar o código é o obfuscamento, na qual você programa de uma maneira que ninguém entende, ou escreve as partes principais de maneira ilegível, ou que pareça fazer uma coisa e faz outra. Existe obfuscadores de código que pegam as variáveis e dão nomes curtos e sem sentido, como s1, s2, i1, i2, ... e amontoam o código, como for i in [1,2,3]: print i; print i+1.