diff --git a/setuptools_golang.py b/setuptools_golang.py index 2b1b71b..8dddd47 100644 --- a/setuptools_golang.py +++ b/setuptools_golang.py @@ -1,6 +1,7 @@ import argparse import contextlib import copy +import errno import os import shlex import shutil @@ -10,6 +11,8 @@ import tempfile from distutils.ccompiler import CCompiler from distutils.dist import Distribution +from types import TracebackType +from typing import Any from typing import Callable from typing import Dict from typing import Generator @@ -22,22 +25,33 @@ from setuptools.command.build_ext import build_ext as _build_ext +def rmtree(path: str) -> None: + """Newer golang uses readonly dirs & files for module cache.""" + def handle_remove_readonly( + func: Callable[..., Any], + path: str, + exc: Tuple[Type[OSError], OSError, TracebackType], + ) -> None: + excvalue = exc[1] + if ( + func in (os.rmdir, os.remove, os.unlink) and + excvalue.errno == errno.EACCES + ): + for p in (path, os.path.dirname(path)): + os.chmod(p, os.stat(p).st_mode | stat.S_IWUSR) + func(path) + else: + raise + shutil.rmtree(path, ignore_errors=False, onerror=handle_remove_readonly) + + @contextlib.contextmanager def _tmpdir() -> Generator[str, None, None]: tempdir = tempfile.mkdtemp() try: yield tempdir finally: - def err( - action: Callable[[str], None], - name: str, - exc: Exception, - ) -> None: # pragma: no cover (windows) - """windows: can't remove readonly files, make them writeable!""" - os.chmod(name, stat.S_IWRITE) - action(name) - - shutil.rmtree(tempdir, onerror=err) + rmtree(tempdir) def _get_cflags( diff --git a/testing/gomodules/go.mod b/testing/gomodules/go.mod new file mode 100644 index 0000000..a622a41 --- /dev/null +++ b/testing/gomodules/go.mod @@ -0,0 +1,5 @@ +module github.com/asottile/setuptools-golang/testing/gomodules + +go 1.14 + +require github.com/golang/example v0.0.0-20170904185048-46695d81d1fa diff --git a/testing/gomodules/go.sum b/testing/gomodules/go.sum new file mode 100644 index 0000000..6cb484e --- /dev/null +++ b/testing/gomodules/go.sum @@ -0,0 +1,2 @@ +github.com/golang/example v0.0.0-20170904185048-46695d81d1fa h1:iqCQC2Z53KkwGgTN9szyL4q0OQHmuNjeoNnMT6lk66k= +github.com/golang/example v0.0.0-20170904185048-46695d81d1fa/go.mod h1:tO/5UvQ/uKigUjQBPqzstj6uxd3fUIjddi19DxGJeWg= diff --git a/testing/gomodules/reversemsg.go b/testing/gomodules/reversemsg.go new file mode 100644 index 0000000..8e39a39 --- /dev/null +++ b/testing/gomodules/reversemsg.go @@ -0,0 +1,19 @@ +package main + +// #include +import "C" + +import ( + "fmt" + + "github.com/golang/example/stringutil" +) + +//export reversemsg +func reversemsg() *C.PyObject { + fmt.Print(stringutil.Reverse("elpmaxe tset")) + + return C.Py_None +} + +func main() {} diff --git a/testing/gomodules/reversemsg_support.go b/testing/gomodules/reversemsg_support.go new file mode 100644 index 0000000..377fd7d --- /dev/null +++ b/testing/gomodules/reversemsg_support.go @@ -0,0 +1,29 @@ +package main + +// #include +// +// PyObject* reversemsg(); +// +// static struct PyMethodDef methods[] = { +// {"reversemsg", (PyCFunction)reversemsg, METH_NOARGS}, +// {NULL, NULL} +// }; +// +// #if PY_MAJOR_VERSION >= 3 +// static struct PyModuleDef module = { +// PyModuleDef_HEAD_INIT, +// "gomodules", +// NULL, +// -1, +// methods +// }; +// +// PyMODINIT_FUNC PyInit_gomodules(void) { +// return PyModule_Create(&module); +// } +// #else +// PyMODINIT_FUNC initgomodules(void) { +// Py_InitModule3("gomodules", methods, NULL); +// } +// #endif +import "C" diff --git a/testing/gomodules/setup.py b/testing/gomodules/setup.py new file mode 100644 index 0000000..793f3f2 --- /dev/null +++ b/testing/gomodules/setup.py @@ -0,0 +1,15 @@ +from setuptools import Extension +from setuptools import setup + + +setup( + name='gomod', + ext_modules=[Extension('gomodules', ['reversemsg.go'])], + build_golang={ + 'root': 'github.com/asottile/setuptools-golang/testing/gomodules', + }, + # Would do this, but we're testing *our* implementation and this would + # install from pypi. We can rely on setuptools-golang being already + # installed under test. + # setup_requires=['setuptools-golang'], +) diff --git a/tests/setuptools_golang_test.py b/tests/setuptools_golang_test.py index eacf9a2..36f02c9 100644 --- a/tests/setuptools_golang_test.py +++ b/tests/setuptools_golang_test.py @@ -103,6 +103,15 @@ def test_integration_imports_gh(venv): assert out == '\x1b[0;31mohai\x1b[0m\n' +GOMOD = 'import gomodules; gomodules.reversemsg()' + + +def test_integration_gomodules(venv): + run(venv.pip, 'install', os.path.join('testing', 'gomodules')) + out = run_output(venv.python, '-c', GOMOD) + assert out == 'test example' + + def test_integration_notfound(venv): ret = run( venv.pip, 'install', os.path.join('testing', 'notfound'),