39

Well, I have a Python package. I need to compile it as dll before distribute it in a way easily importable. How? You may suggest that *.pyc. But I read somewhere any *.pyc can be easily decompiled!

Update: Follow these:
1) I wrote a python package
2) want to distribute it
3) do NOT want distribute the source
4) *.pyc is decompilable >> source can be extracted!
5) dll is standard

Developer
  • 8,258
  • 8
  • 49
  • 58
  • 6
    All a .pyc is is a python script compiled to bytecode so it loads in the interpreter faster. It's not an executable. Why do you need the python package "compiled as a dll"? – Joel Cornett Jun 02 '12 at 02:36
  • 2
    All your answer is already given in the question! dll is not de-compilable to source code compared to *.pyc, right? – Developer Jun 02 '12 at 09:12

11 Answers11

16

Nowadays a simple solutino exists: use Nuitka compiler as described in Nuitka User Manual

Use Case 2 - Extension Module compilation If you want to compile a single extension module, all you have to do is this:

 python -m nuitka --module some_module.py 

The resulting file some_module.so can then be used instead of some_module.py.

You need to compile for each platform you want to support and write some initialization code to import so/pyd file ~~appropriate for given platform/python version etc.~~

[EDIT 2021-12]: Actually in python 3 the proper so/dll is determined automatically based on the file name (if it includes python version and platform - can't find PEP for this feature at the moment but Nuitka creates proper names for compiled modules). So for python 2.7 the library name would be something.pyd or something.so whereas for python 3 this would change to something.cp36-win32.pyd or something.cpython-36m-x86_64-linux-gnu.so (for 32bit python 3.6 on x86).

The result is not DLL as requested but Python-native compiled binary format (it is not bytecode like in pyc files; the so/pyd format cannot be easily decompiled - Nuitka compiles to machine code through C++ translation)

EDIT [2020-01]: The compiled module is prone to evaluation methods using python standard mechanisms - e.g. it can be imported as any other module and get its methods listed etc. To secure implementation from being exposed that way there is more work to be done than just compiling to a binary module.

Mr. Girgitt
  • 2,853
  • 1
  • 19
  • 22
13

Write everything you want to hide in Cython, and compile it to pyd. That's as close as you can get to making compiled python code.

Also, dll is not a standard, not in Python world. They're not portable, either.

Kien Truong
  • 11,179
  • 2
  • 30
  • 36
  • You know, I have a module written in Python. If I import it in another script there will be a *.pyc. So it is simple however people can convert *.pyc to the source code which I don't want to allow them. Rewriting the module in Cython shouldn't be easy task, am I right? BTW, in Windows dll is rigid standard, my client uses only Windows. – Developer Jun 03 '12 at 12:21
  • Do you know how to use a dll from Python ? – Kien Truong Jun 04 '12 at 05:01
  • Tricky question! I know there are difficulties there. Oh, my gosh, I need just to not deliver the source. How to do!? I know *.pyc is just ready to import, no headache. I can imagine how difficult is going to be a dll, now. – Developer Jun 04 '12 at 11:05
  • using something like py2exe wouldn't make it any harder to reverse engineer than just shipping a .pyc file – patmanpato Jun 15 '15 at 06:31
  • 3
    But dlls are standard in the Windows world. – rhody Mar 01 '18 at 01:37
  • ,@Developer You might want to check out ctypes if you want to manipulate DLLs, it's not much fun though – Xantium Mar 04 '19 at 09:31
7

You can use py2exe.org to convert python scripts into windows executables. Granted this will only work on windows, but it's better then nothing.

jb.
  • 9,921
  • 12
  • 54
  • 90
  • 1
    from package I mean a package (module) which means I may distribute some Python codes but for some modules I want only distribute dll files. – Developer Jun 02 '12 at 09:18
  • I could be wrong so don't quote me on this, but .exe files can be used just like a dll module. so use py2exe to create the .exe, then just rename it to a .dll so your users don't try to "run" a package. – jb. Jun 02 '12 at 15:40
  • I think your idea is worth to try. Thanks – Developer Jun 03 '12 at 12:16
  • exe files are not DLLs unfortunately, they are different things. In C/C++ coding DLL files lack the `main()` function – Xantium Mar 04 '19 at 09:28
  • DLLs have DllMain which handles the process and thread attachment and detachment events. If you compile to an EXE you won't be able to hook into those. – Fax Nov 28 '19 at 14:18
6

You can embed python inside C. The real trick is converting between C values and Python values. Once you've done that, though, making a DLL is pretty straightforward.

However, why do you need to make a dll? Do you need to use this from a non-python program?

Community
  • 1
  • 1
Nick ODell
  • 15,465
  • 3
  • 32
  • 66
  • 2
    Given in the question: 1) dll is portable, 2) no source code distributed. – Developer Jun 02 '12 at 09:12
  • 1
    This idea, the first part doesn't look easy. Is there any application that automatically embed my Python module and give me an importable dll? – Developer Jun 03 '12 at 12:25
  • 1
    What do you mean by 'importable' dll? Importable from where? C++? Python? INTERCAL? – Nick ODell Jun 03 '12 at 18:00
  • Thanks for the link. Interesting! I asked exactly the same question. There is 118 upvotes here -3! Really funny world. – Developer Jun 05 '12 at 02:03
6

Python embedding is supported in CFFI version 1.5, you can create a .dll file which can be used by a Windows C application.

Ken Hung
  • 752
  • 5
  • 13
  • If you have a DllMain, you can declare it as "CFFI_DLLEXPORT int __stdcall DllMain(void* hinstDLL, unsigned long fdwReason, void* lpvReserved);" in your header file. – Fax Nov 29 '19 at 08:45
3

I would also using Cython to generate pyd files, like Dikei wrote. But if you really want to secure your code, you should better write the important stuff in C++. The best would be to combine both C++ and Python. The idea: you would leave the python code open for adjustments, so that you don't have to compile everything over and over again. That means, you would write the "core" in C++ (which is the most secure solution these days) and use those dll files in your python code. It really depends what kind of tool or program you are building and how you want to execute it. I create mostly an execution file (exe,app) once I finish a tool or a program, but this is more for the end user. This could be done with py2exe and py2app (both 64 bit compatible). If you implement the interpreter, the end user's machine doesn't have to have python installed on the system.

A pyd file is the same like a dll and fully supported inside python. So you can normally import your module. You can find more information about it here.

Using and generating pyd files is the fastest and easiest way to create safe and portable python code.

You could also write real dll files in C++ and import them with ctypes to use them (here a good post and here the python description of how it works)

Salvioner
  • 303
  • 5
  • 16
MagSec
  • 346
  • 4
  • 17
3

To expand on the answer by Nick ODell

You must be on Windows for DLLs to work, they are not portable.

However the code below is cross platform and all platforms support run-times so this can be re-compiled for each platform you need it to work on.

Python does not (yet) provide an easy tool to create a dll, however you can do it in C/C++


First you will need a compiler (Windows does not have one by default) notably Cygwin, MinGW or Visual Studio.

A basic knowledge of C is also necessary (since we will be coding mainly in C).

You will also need to include the necessary headers, I will skip this so it does not become horribly long, and will assume everything is set up correctly.


For this demonstration I will print a traditional hello world:

Python code we will be converting to a DLL:

def foo(): print("hello world")

C code:

#include "Python.h" // Includes everything to use the Python-C API

int foo(void); // Declare foo

int foo(void) { // Name of our function in our DLL

    Py_Initialize(); // Initialise Python

    PyRun_SimpleString("print('hello world')"); // Run the Python commands

    return 0; // Finish execution
}

Here is the tutorial for embedding Python. There are a few extra things that should be added here, but for brevity I have left those out.

Compile it and you should have a DLL. :)


That is not all. You will need to distribute whatever dependencies are needed, that will mean the python36.dll run-time and some other components to run the Python script.

My C coding is not perfect, so if anyone can spot any improvements please comment and I will do my best to fix the it.


It might also be possible in C# from this answer How do I call a specific Method from a Python Script in C#?, since C# can create DLLs, and you can call Python functions from C#.

Xantium
  • 11,201
  • 10
  • 62
  • 89
0

You can use pyinstaller for converting the .py files into executable with all required packages into .dll format.

Step 1. pip install pyinstaller,

step 2. new python file let's name it code.py .

step 3. Write some lines of code i.e print("Hello World")

step 4. Open Command Prompt in the same location and write pyinstaller code.py hit enter. Last Step see in the same location two folders name build, dist will be created. inside dist folder there is folder code and inside that folder there is an exe file code.exe along with required .dll files.

Xantium
  • 11,201
  • 10
  • 62
  • 89
Adnan Mohib
  • 331
  • 5
  • 16
  • interesting - can you provide some code to show how it's done? – Ferdinando Randisi May 18 '18 at 20:59
  • 1
    **Step 1.** `pip install pyinstaller`, **step 2.** new python file let's name it `code.py` . **step 3.** Write some lines of code i.e `print("Hello World")` **step 4.** Open Command Prompt in the same location and write `pyinstaller code.py` hit enter. **Last Step** see in the same location two folders name `build, dist` will be created. inside dist folder there is folder `code` and inside that folder there is an exe file `code.exe` along with required .dll files. – Adnan Mohib May 22 '18 at 12:50
  • 1
    That creates an exe not a DLL, the DLL are used to run the exe – Xantium Mar 04 '19 at 09:26
0

This is my idea, it might work. I don't know, if that work or not.

1.Create your *.py files.
2.Rename them into *.pyx
3.Convert them into *.c files using Cython
4.Compile *.c into *.dll files.

But I don't recommend you because it won't work on any other platforms, except Windows.

Sairam
  • 375
  • 1
  • 5
  • 28
0

If your only goal is to hide your source code, it is much simpler to just compile your code to an executable(use PyInstaller, for example), and use an module with readable source for communication. NOTE: You might need more converter functions as shown in this example. Example: Module:

import subprocess
import codecs
def _encode_str(str):
    encoded=str.encode("utf-32","surrogatepass")
    return codecs.encode(encoded,"base64").replace(b"\n",b"")
def _decode_str(b64):
    return codecs.decode(b64,"base64").decode("utf-32","surrogatepass")
def strlen(s:str):#return length of str;int
    proc=subprocess.Popen(["path_to_your_exe.exe","strlen",_encode_str(str).decode("ascii")],stdout=subprocess.PIPE)
    return int(proc.stdout.read())
def random_char_from_string(str):
    proc=subprocess.Popen(["path_to_your_exe.exe","randchr",_encode_str(str).decode("ascii")],stdout=subprocess.PIPE)
    return _decode_str(proc.stdout.read())

Executable:

import sys
import codecs
import random
def _encode_str(str):
    encoded=str.encode("utf-32","surrogatepass")
    return codecs.encode(encoded,"base64").replace(b"\n",b"")
def _decode_str(b64):
    return codecs.decode(b64,"base64").decode("utf-32","surrogatepass")
command=sys.argv[1]
if command=="strlen":
    s=_decode_str(sys.argv[2].encode("ascii"))
    print(len(str))
if command=="randchr":
    s_decode_str(sys.argv[2].encode("ascii"))
    print(_encode_str(random.choice(s)).decode("ascii"))

You might also want to think about compiling different executables for different platforms, if your package isn't a windows-only package anyways.

HelpfulHelper
  • 226
  • 2
  • 5
-3

Grab Visual Studio Express and IronPython and do it that way? You'll be in Python 2.7.6 world though.

Alan B
  • 4,086
  • 24
  • 33