-
Notifications
You must be signed in to change notification settings - Fork 5.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make python bindings article partially work on win #157
Changes from all commits
1592379
2d14255
38a3852
2a058b0
4ea1d0f
fbe3d70
507b07c
fc9ad84
82d33bb
8a04327
c097898
e6a6a70
e07259e
3740d6f
30800e1
c17f314
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Real Python - Python Bindings Sample Code Repo | ||
|
||
This is the repo to accompany the [Python Bindings](https://realpython.com/python-bindings-overview/) article. | ||
|
||
To be able to run the code, you must first install the requirements: | ||
|
||
```console | ||
$ python -m pip install -r requirements.txt | ||
``` | ||
This should be done inside a virtual environment. | ||
|
||
Once that is installed, you can use the invoke tool mentioned in the article to build and run the tests. See the tasks.py file or run invoke --list to get more details. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,7 @@ | ||
float cmult(int int_param, float float_param); | ||
#ifdef _MSC_VER | ||
#define EXPORT_SYMBOL __declspec(dllexport) | ||
#else | ||
#define EXPORT_SYMBOL | ||
#endif | ||
|
||
EXPORT_SYMBOL float cmult(int int_param, float float_param); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,93 @@ | ||
""" Task definitions for invoke command line utility for python bindings | ||
overview article. """ | ||
overview article. | ||
""" | ||
import cffi | ||
import invoke | ||
import pathlib | ||
import sys | ||
import os | ||
import shutil | ||
import re | ||
import glob | ||
|
||
on_win = sys.platform.startswith("win") | ||
|
||
|
||
@invoke.task | ||
def clean(c): | ||
""" Remove any built objects """ | ||
for pattern in ["*.o", "*.so", "cffi_example* cython_wrapper.cpp"]: | ||
c.run("rm -rf {}".format(pattern)) | ||
for file_pattern in ( | ||
"*.o", | ||
"*.so", | ||
"*.obj", | ||
"*.dll", | ||
"*.exp", | ||
"*.lib", | ||
"*.pyd", | ||
"cffi_example*", # Is this a dir? | ||
"cython_wrapper.cpp", | ||
): | ||
for file in glob.glob(file_pattern): | ||
os.remove(file) | ||
for dir_pattern in "Release": | ||
for dir in glob.glob(dir_pattern): | ||
shutil.rmtree(dir) | ||
|
||
|
||
def print_banner(msg): | ||
print("==================================================") | ||
print("= {} ".format(msg)) | ||
|
||
|
||
@invoke.task | ||
def build_cmult(c): | ||
@invoke.task() | ||
def build_cmult(c, path=None): | ||
""" Build the shared library for the sample C code """ | ||
print_banner("Building C Library") | ||
invoke.run("gcc -c -Wall -Werror -fpic cmult.c -I /usr/include/python3.7") | ||
invoke.run("gcc -shared -o libcmult.so cmult.o") | ||
print("* Complete") | ||
# Moving this type hint into signature causes an error (???) | ||
c: invoke.Context | ||
if on_win: | ||
if not path: | ||
print("Path is missing") | ||
else: | ||
# Using c.cd didn't work with paths that have spaces :/ | ||
path = f'"{path}vcvars32.bat" x86' # Enter the VS venv | ||
path += f'&& cd "{os.getcwd()}"' # Change to current dir | ||
path += "&& cl /LD cmult.c" # Compile | ||
# Uncomment line below, to suppress stdout | ||
# path = path.replace("&&", " >nul &&") + " >nul" | ||
c.run(path) | ||
else: | ||
print_banner("Building C Library") | ||
cmd = "gcc -c -Wall -Werror -fpic cmult.c -I /usr/include/python3.7" | ||
invoke.run(cmd) | ||
invoke.run("gcc -shared -o libcmult.so cmult.o") | ||
print("* Complete") | ||
|
||
|
||
@invoke.task(build_cmult) | ||
@invoke.task() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is the build on windows taking significant time? If not, I'd prefer to leave the dependency here. It makes the individual targets a bit more fool-proof. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it doesn't. On windows, the problem is that it'll only build if we supply the argument pointing to visual studio. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ahhh. I get it. That's fine. I missed that point. |
||
def test_ctypes(c): | ||
""" Run the script to test ctypes """ | ||
print_banner("Testing ctypes Module") | ||
invoke.run("python3 ctypes_test.py", pty=True) | ||
# pty and python3 didn't work for me (win). | ||
if on_win: | ||
invoke.run("python ctypes_test.py") | ||
else: | ||
invoke.run("python3 ctypes_test.py", pty=True) | ||
|
||
|
||
@invoke.task(build_cmult) | ||
@invoke.task() | ||
def build_cffi(c): | ||
""" Build the CFFI Python bindings """ | ||
print_banner("Building CFFI Module") | ||
ffi = cffi.FFI() | ||
|
||
this_dir = pathlib.Path().absolute() | ||
this_dir = pathlib.Path().resolve() | ||
h_file_name = this_dir / "cmult.h" | ||
with open(h_file_name) as h_file: | ||
ffi.cdef(h_file.read()) | ||
# cffi does not like our preprocessor directives, so we remove them | ||
lns = h_file.read().splitlines() | ||
flt = filter(lambda ln: not re.match(r" *#", ln), lns) | ||
flt = map(lambda ln: ln.replace("EXPORT_SYMBOL ", ""), flt) | ||
ffi.cdef(str("\n").join(flt)) | ||
|
||
ffi.set_source( | ||
"cffi_example", | ||
|
@@ -66,7 +111,7 @@ def build_cffi(c): | |
def test_cffi(c): | ||
""" Run the script to test CFFI """ | ||
print_banner("Testing CFFI Module") | ||
invoke.run("python3 cffi_test.py", pty=True) | ||
invoke.run("python cffi_test.py", pty=not on_win) | ||
|
||
|
||
@invoke.task() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is because it doesn't exist when you start out. :) CFFI creates the module when you run the build.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, but my IDE, PyCharm, didn't want to acknowledge it, even after making it. I actually lost a lot of time figuring out what the problem is, since I didn't even bother to try and run it, lol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's odd. I wonder what pycharm is doing, but not enough to actually go find out, you know. :)