Skip to content

Commit fd7c6c3

Browse files
committed
Merge pull request asottile-archive#2 from asottile/support_imports
Support imports
2 parents 5350377 + a6806ce commit fd7c6c3

File tree

5 files changed

+141
-14
lines changed

5 files changed

+141
-14
lines changed

setuptools_golang.py

+42-14
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
from __future__ import print_function
22
from __future__ import unicode_literals
33

4+
import contextlib
45
import distutils.sysconfig
56
import os
67
import pipes
8+
import shutil
79
import subprocess
810
import sys
11+
import tempfile
912

1013
from setuptools.command.build_ext import build_ext as _build_ext
1114

1215

1316
PYPY = '__pypy__' in sys.builtin_module_names
1417

1518

19+
def _get_cflags(compiler):
20+
return ' '.join('-I{}'.format(p) for p in compiler.include_dirs)
21+
22+
1623
def _get_ldflags_pypy():
1724
if PYPY: # pragma: no cover (pypy only)
1825
return '-L{} -lpypy-c'.format(
@@ -60,6 +67,15 @@ def _print_cmd(env, cmd):
6067
)
6168

6269

70+
@contextlib.contextmanager
71+
def _tmpdir():
72+
tempdir = tempfile.mkdtemp()
73+
try:
74+
yield tempdir
75+
finally:
76+
shutil.rmtree(tempdir)
77+
78+
6379
class build_ext(_build_ext):
6480
def build_extension(self, ext):
6581
# If there are no .go files then the parent should handle this
@@ -88,20 +104,32 @@ def build_extension(self, ext):
88104
source_dir, = source_dirs
89105
source_dir = os.path.abspath(source_dir)
90106

91-
env = {
92-
'CGO_CFLAGS': ' '.join(
93-
'-I{}'.format(p) for p in self.compiler.include_dirs
94-
),
95-
'CGO_LDFLAGS': _get_ldflags(),
96-
}
97-
cmd = (
98-
'go', 'build', '-buildmode=c-shared',
99-
'-o', os.path.abspath(self.get_ext_fullpath(ext.name)),
100-
)
101-
_print_cmd(env, cmd)
102-
subprocess.check_call(
103-
cmd, cwd=source_dir, env=dict(os.environ, **env),
104-
)
107+
# Copy the package into a temporary GOPATH environment
108+
with _tmpdir() as tempdir:
109+
srcdir = os.path.join(tempdir, 'src')
110+
os.mkdir(srcdir)
111+
pkg_path = os.path.join(srcdir, '_mypkg')
112+
shutil.copytree(source_dir, pkg_path)
113+
114+
env = {
115+
'GOPATH': tempdir,
116+
'CGO_CFLAGS': _get_cflags(self.compiler),
117+
'CGO_LDFLAGS': _get_ldflags(),
118+
}
119+
cmd_get = ('go', 'get')
120+
_print_cmd(env, cmd_get)
121+
subprocess.check_call(
122+
cmd_get, cwd=pkg_path, env=dict(os.environ, **env),
123+
)
124+
125+
cmd_build = (
126+
'go', 'build', '-buildmode=c-shared',
127+
'-o', os.path.abspath(self.get_ext_fullpath(ext.name)),
128+
)
129+
_print_cmd(env, cmd_build)
130+
subprocess.check_call(
131+
cmd_build, cwd=pkg_path, env=dict(os.environ, **env),
132+
)
105133

106134

107135
def set_build_ext(dist, attr, value):

setuptools_golang_test.py

+9
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,15 @@ def test_integration_project_with_c(venv):
128128
assert out == 'hello world\n'
129129

130130

131+
RED = 'import red; print(red.red(u"ohai"))'
132+
133+
134+
def test_integration_imports_gh(venv):
135+
run(venv.pip, 'install', 'testing/imports_gh')
136+
out = run_output(venv.python, '-c', RED)
137+
assert out == '\x1b[31mohai\x1b[0m\n'
138+
139+
131140
def test_integration_notfound(venv):
132141
ret = run(
133142
venv.pip, 'install', 'testing/notfound',

testing/imports_gh/red.c

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#include <Python.h>
2+
3+
#if PY_MAJOR_VERSION >= 3
4+
#define PyRed_Bytes_AS_STRING PyBytes_AS_STRING
5+
#else
6+
#define PyRed_Bytes_AS_STRING PyString_AS_STRING
7+
#endif
8+
9+
/* Will come from go */
10+
PyObject* red(PyObject*);
11+
12+
/* To shim go's missing variadic function support */
13+
int PyArg_ParseTuple_U(PyObject* args, PyObject** obj) {
14+
return PyArg_ParseTuple(args, "U", obj);
15+
}
16+
17+
/* To shim go's lack of C macro support */
18+
void PyRed_DECREF(PyObject* obj) {
19+
Py_DECREF(obj);
20+
}
21+
22+
const char* PyRed_Bytes_AsString(PyObject* s) {
23+
return PyRed_Bytes_AS_STRING(s);
24+
}
25+
26+
static struct PyMethodDef methods[] = {
27+
{"red", (PyCFunction)red, METH_VARARGS},
28+
{NULL, NULL}
29+
};
30+
31+
#if PY_MAJOR_VERSION >= 3
32+
static struct PyModuleDef module = {
33+
PyModuleDef_HEAD_INIT,
34+
"red",
35+
NULL,
36+
-1,
37+
methods
38+
};
39+
40+
PyMODINIT_FUNC PyInit_red(void) {
41+
return PyModule_Create(&module);
42+
}
43+
#else
44+
PyMODINIT_FUNC initred(void) {
45+
Py_InitModule3("red", methods, NULL);
46+
}
47+
#endif

testing/imports_gh/red.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package main
2+
3+
// #include <stdlib.h>
4+
// #include <Python.h>
5+
// int PyArg_ParseTuple_U(PyObject*, PyObject**);
6+
// const char* PyRed_Bytes_AsString(PyObject*);
7+
// void PyRed_DECREF(PyObject*);
8+
import "C"
9+
import "unsafe"
10+
import "github.com/mgutz/ansi"
11+
12+
//export red
13+
func red(self *C.PyObject, args *C.PyObject) *C.PyObject {
14+
var obj *C.PyObject
15+
if C.PyArg_ParseTuple_U(args, &obj) == 0 {
16+
return nil
17+
}
18+
bytes := C.PyUnicode_AsUTF8String(obj)
19+
cstr := C.PyRed_Bytes_AsString(bytes)
20+
red := ansi.Color(C.GoString(cstr), "red")
21+
cstr = C.CString(red)
22+
ret := C.PyUnicode_FromString(cstr)
23+
24+
C.free(unsafe.Pointer(cstr))
25+
C.PyRed_DECREF(bytes)
26+
27+
return ret
28+
}
29+
30+
func main() {}

testing/imports_gh/setup.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from setuptools import Extension
2+
from setuptools import setup
3+
4+
5+
setup(
6+
name='imports-gh',
7+
ext_modules=[Extension('red', ['red.go'])],
8+
build_golang=True,
9+
# Would do this, but we're testing *our* implementation and this would
10+
# install from pypi. We can rely on setuptools-golang being already
11+
# installed under test.
12+
# setup_requires=['setuptools-golang'],
13+
)

0 commit comments

Comments
 (0)