Skip to content

Commit a14a6fc

Browse files
authored
Merge pull request #157 from Znunu/master
Make python bindings article partially work on win
2 parents 2b5fb16 + c17f314 commit a14a6fc

File tree

5 files changed

+85
-20
lines changed

5 files changed

+85
-20
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Real Python - Python Bindings Sample Code Repo
2+
3+
This is the repo to accompany the [Python Bindings](https://realpython.com/python-bindings-overview/) article.
4+
5+
To be able to run the code, you must first install the requirements:
6+
7+
```console
8+
$ python -m pip install -r requirements.txt
9+
```
10+
This should be done inside a virtual environment.
11+
12+
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.

python-bindings/overview_article/cffi_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env python3
2+
# IDE might complain with "no module found" here, even when it exists
23
import cffi_example
34

45
if __name__ == "__main__":
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
float cmult(int int_param, float float_param);
1+
#ifdef _MSC_VER
2+
#define EXPORT_SYMBOL __declspec(dllexport)
3+
#else
4+
#define EXPORT_SYMBOL
5+
#endif
6+
7+
EXPORT_SYMBOL float cmult(int int_param, float float_param);

python-bindings/overview_article/ctypes_test.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
#!/usr/bin/env python
22
""" Simple examples of calling C functions through ctypes module. """
33
import ctypes
4-
import pathlib
5-
4+
import sys
65

76
if __name__ == "__main__":
87
# Load the shared library into c types.
9-
libname = pathlib.Path().absolute() / "libcmult.so"
10-
c_lib = ctypes.CDLL(libname)
8+
if sys.platform.startswith("win"):
9+
c_lib = ctypes.CDLL("cmult.dll")
10+
else:
11+
c_lib = ctypes.CDLL("libcmult.so")
1112

1213
# Sample data for our call:
1314
x, y = 6, 2.3

python-bindings/overview_article/tasks.py

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,93 @@
11
""" Task definitions for invoke command line utility for python bindings
2-
overview article. """
2+
overview article.
3+
"""
34
import cffi
45
import invoke
56
import pathlib
7+
import sys
8+
import os
9+
import shutil
10+
import re
11+
import glob
12+
13+
on_win = sys.platform.startswith("win")
614

715

816
@invoke.task
917
def clean(c):
1018
""" Remove any built objects """
11-
for pattern in ["*.o", "*.so", "cffi_example* cython_wrapper.cpp"]:
12-
c.run("rm -rf {}".format(pattern))
19+
for file_pattern in (
20+
"*.o",
21+
"*.so",
22+
"*.obj",
23+
"*.dll",
24+
"*.exp",
25+
"*.lib",
26+
"*.pyd",
27+
"cffi_example*", # Is this a dir?
28+
"cython_wrapper.cpp",
29+
):
30+
for file in glob.glob(file_pattern):
31+
os.remove(file)
32+
for dir_pattern in "Release":
33+
for dir in glob.glob(dir_pattern):
34+
shutil.rmtree(dir)
1335

1436

1537
def print_banner(msg):
1638
print("==================================================")
1739
print("= {} ".format(msg))
1840

1941

20-
@invoke.task
21-
def build_cmult(c):
42+
@invoke.task()
43+
def build_cmult(c, path=None):
2244
""" Build the shared library for the sample C code """
23-
print_banner("Building C Library")
24-
invoke.run("gcc -c -Wall -Werror -fpic cmult.c -I /usr/include/python3.7")
25-
invoke.run("gcc -shared -o libcmult.so cmult.o")
26-
print("* Complete")
45+
# Moving this type hint into signature causes an error (???)
46+
c: invoke.Context
47+
if on_win:
48+
if not path:
49+
print("Path is missing")
50+
else:
51+
# Using c.cd didn't work with paths that have spaces :/
52+
path = f'"{path}vcvars32.bat" x86' # Enter the VS venv
53+
path += f'&& cd "{os.getcwd()}"' # Change to current dir
54+
path += "&& cl /LD cmult.c" # Compile
55+
# Uncomment line below, to suppress stdout
56+
# path = path.replace("&&", " >nul &&") + " >nul"
57+
c.run(path)
58+
else:
59+
print_banner("Building C Library")
60+
cmd = "gcc -c -Wall -Werror -fpic cmult.c -I /usr/include/python3.7"
61+
invoke.run(cmd)
62+
invoke.run("gcc -shared -o libcmult.so cmult.o")
63+
print("* Complete")
2764

2865

29-
@invoke.task(build_cmult)
66+
@invoke.task()
3067
def test_ctypes(c):
3168
""" Run the script to test ctypes """
3269
print_banner("Testing ctypes Module")
33-
invoke.run("python3 ctypes_test.py", pty=True)
70+
# pty and python3 didn't work for me (win).
71+
if on_win:
72+
invoke.run("python ctypes_test.py")
73+
else:
74+
invoke.run("python3 ctypes_test.py", pty=True)
3475

3576

36-
@invoke.task(build_cmult)
77+
@invoke.task()
3778
def build_cffi(c):
3879
""" Build the CFFI Python bindings """
3980
print_banner("Building CFFI Module")
4081
ffi = cffi.FFI()
4182

42-
this_dir = pathlib.Path().absolute()
83+
this_dir = pathlib.Path().resolve()
4384
h_file_name = this_dir / "cmult.h"
4485
with open(h_file_name) as h_file:
45-
ffi.cdef(h_file.read())
86+
# cffi does not like our preprocessor directives, so we remove them
87+
lns = h_file.read().splitlines()
88+
flt = filter(lambda ln: not re.match(r" *#", ln), lns)
89+
flt = map(lambda ln: ln.replace("EXPORT_SYMBOL ", ""), flt)
90+
ffi.cdef(str("\n").join(flt))
4691

4792
ffi.set_source(
4893
"cffi_example",
@@ -66,7 +111,7 @@ def build_cffi(c):
66111
def test_cffi(c):
67112
""" Run the script to test CFFI """
68113
print_banner("Testing CFFI Module")
69-
invoke.run("python3 cffi_test.py", pty=True)
114+
invoke.run("python cffi_test.py", pty=not on_win)
70115

71116

72117
@invoke.task()

0 commit comments

Comments
 (0)