diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 2a9fb1b..0000000 --- a/.coveragerc +++ /dev/null @@ -1,29 +0,0 @@ -[run] -parallel = True -branch = True -source = $TOP -data_file = $TOP/.coverage -omit = - .tox/* - /usr/* - */tmp* - setup.py - -[report] -exclude_lines = - # Have to re-enable the standard pragma - \#\s*pragma: no cover - - # Don't complain if tests don't hit defensive assertion code: - ^\s*raise AssertionError\b - ^\s*raise NotImplementedError\b - ^\s*return NotImplemented\b - ^\s*raise$ - - # Don't complain if non-runnable code isn't run: - ^if __name__ == ['"]__main__['"]:$ - -[html] -directory = coverage-html - -# vim:ft=dosini diff --git a/.github/actions/pre-test/action.yml b/.github/actions/pre-test/action.yml new file mode 100644 index 0000000..560b489 --- /dev/null +++ b/.github/actions/pre-test/action.yml @@ -0,0 +1,4 @@ +runs: + using: composite + steps: + - uses: actions/setup-go@v3 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..fabc8af --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,19 @@ +name: main + +on: + push: + branches: [main, test-me-*] + tags: '*' + pull_request: + +jobs: + main-windows: + uses: asottile/workflows/.github/workflows/tox.yml@v1.7.0 + with: + env: '["py39"]' + os: windows-latest + main-linux: + uses: asottile/workflows/.github/workflows/tox.yml@v1.7.0 + with: + env: '["pypy3", "py39", "py310", "py311", "py312", "py313"]' + os: ubuntu-latest diff --git a/.gitignore b/.gitignore index 45c9677..978f284 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,7 @@ *.egg-info -*.iml -*.py[cod] +*.pyc *.so -.*.sw[a-z] -.coverage -.tox -.venv.touch -/.cache +/.coverage +/.tox /build -/venv* -coverage-html -dist +/dist diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 26efa14..061fab0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,22 +1,49 @@ -- repo: https://github.com/pre-commit/pre-commit-hooks.git - sha: v0.7.0 +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - - id: autopep8-wrapper - - id: check-docstring-first - id: check-yaml - id: debug-statements + - id: double-quote-string-fixer + - id: name-tests-test - id: requirements-txt-fixer - - id: flake8 -- repo: https://github.com/asottile/reorder_python_imports.git - sha: v0.3.1 +- repo: https://github.com/asottile/setup-cfg-fmt + rev: v2.7.0 + hooks: + - id: setup-cfg-fmt +- repo: https://github.com/asottile/reorder-python-imports + rev: v3.14.0 hooks: - id: reorder-python-imports + args: [--py39-plus, --add-import, 'from __future__ import annotations'] +- repo: https://github.com/asottile/add-trailing-comma + rev: v3.1.0 + hooks: + - id: add-trailing-comma +- repo: https://github.com/asottile/pyupgrade + rev: v3.19.1 + hooks: + - id: pyupgrade + args: [--py39-plus] +- repo: https://github.com/hhatto/autopep8 + rev: v2.3.1 + hooks: + - id: autopep8 +- repo: https://github.com/PyCQA/flake8 + rev: 7.1.1 + hooks: + - id: flake8 +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.14.1 + hooks: + - id: mypy + exclude: ^testing/ - repo: local hooks: - id: gofmt name: gofmt language: system entry: gofmt -l -w - files: \.go$ + types: [go] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f2e3337..0000000 --- a/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -language: python -sudo: false -matrix: - include: - - env: TOXENV=py27 GO=1.6 - - env: TOXENV=py27 GO=1.7 - - env: TOXENV=py35 GO=1.7 - python: 3.5 - - env: TOXENV=py36 GO=1.7 - python: 3.6 - - env: TOXENV=pypy GO=1.7 - python: pypy -install: - - eval "$(gimme $GO)" - - pip install coveralls tox -script: tox -after_success: coveralls -cache: - directories: - - $HOME/.cache/pip - - $HOME/.pre-commit diff --git a/Makefile b/Makefile deleted file mode 100644 index a27d50a..0000000 --- a/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -REBUILD_FLAG = - -.PHONY: all -all: venv test - -.PHONY: venv -venv: .venv.touch - tox -e venv $(REBUILD_FLAG) - -.PHONY: test -test: .venv.touch - tox $(REBUILD_FLAG) - -.venv.touch: setup.py requirements-dev.txt - $(eval REBUILD_FLAG := --recreate) - touch .venv.touch - -.PHONY: clean -clean: - find -name '*.pyc' -delete - rm -rf .tox - rm -rf ./venv-* - rm -f .venv.touch diff --git a/README.md b/README.md index 184c5f5..6ed218b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,15 @@ -[![Build Status](https://travis-ci.org/asottile/setuptools-golang.svg?branch=master)](https://travis-ci.org/asottile/setuptools-golang) -[![Coverage Status](https://img.shields.io/coveralls/asottile/setuptools-golang.svg?branch=master)](https://coveralls.io/r/asottile/setuptools-golang) +# DEPRECATED + +it turns out multiple go shared objects in a single process is not supported + +it likely broke in [go 1.21] and there is no intention to fix it :( + +[go 1.21]: https://github.com/golang/go/issues/65050#issue-2074509727 + +___ + +[![build status](https://github.com/asottile/setuptools-golang/actions/workflows/main.yml/badge.svg)](https://github.com/asottile/setuptools-golang/actions/workflows/main.yml) +[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/asottile/setuptools-golang/main.svg)](https://results.pre-commit.ci/latest/github/asottile/setuptools-golang/main) setuptools-golang ================= @@ -8,10 +18,15 @@ A setuptools extension for building cpython extensions written in golang. ## Requirements -This requires golang >= 1.5. It is currently tested against 1.6 and 1.7. +This requires golang >= 1.5. -This requires python >= 2.7. It is currently tested against 2.7, 3.5, 3.6, -and pypy. +This requires python >= 3.7. It is currently tested against python3 and pypy3. + +## Platform Support + +- linux +- macOS +- win32 ## Usage @@ -19,6 +34,11 @@ Add `setuptools-golang` to the `setup_requires` in your setup.py and `build_golang={'root': ...}`. `root` refers to the root go import path of your project. +By default, `setuptools-golang` will strip all binaries. This can be disabled +by adding `'strip': False` to `build_golang`. This will increase the size of +the extension, but the binaries contain debugging information and symbols. + + An extension must be a single file in the `main` go package (though the entire `main` package will be built into the extension). That package may import other code. @@ -60,6 +80,30 @@ Double check that your import is correct. You've probably mistyped an import. Double check that your import is correct. +### `duplicate symbol _XXX in: _cgo_export.o mod.cgo2.o` + +For example: +``` +# github.com/asottile/dockerfile/pylib +duplicate symbol _PyDockerfile_GoParseError in: + $WORK/github.com/asottile/dockerfile/pylib/_obj/_cgo_export.o + $WORK/github.com/asottile/dockerfile/pylib/_obj/main.cgo2.o +``` + +Make sure to mark global variables defined in C as `extern`. +[Here's an example PR](https://github.com/asottile/dockerfile/pull/8) + +### repeated rebuilds can be slow + +setuptools-golang attempts to make builds more repeatable by using a separate +`GOPATH` -- if you'd like to reuse a GOPATH you can set the +`SETUPTOOLS_GOLANG_GOPATH` environment variable: + +```console +$ SETUPTOOLS_GOLANG_GOPATH=~/go pip install . +... +``` + ## Building manylinux wheels `setuptools-golang` also provides a tool for building @@ -78,7 +122,6 @@ $ setuptools-golang-build-manylinux-wheels total 8092 drwxrwxr-x 2 1000 1000 4096 Feb 1 04:16 . drwxr-xr-x 41 root root 4096 Feb 1 04:15 .. --rw-r--r-- 1 1000 1000 2065095 Feb 1 04:16 setuptools_golang_examples-0.1.1-cp27-cp27mu-manylinux1_x86_64.whl -rw-r--r-- 1 1000 1000 2063299 Feb 1 04:16 setuptools_golang_examples-0.1.1-cp34-cp34m-manylinux1_x86_64.whl -rw-r--r-- 1 1000 1000 2064862 Feb 1 04:16 setuptools_golang_examples-0.1.1-cp35-cp35m-manylinux1_x86_64.whl -rw-r--r-- 1 1000 1000 2064873 Feb 1 04:16 setuptools_golang_examples-0.1.1-cp36-cp36m-manylinux1_x86_64.whl diff --git a/requirements-dev.txt b/requirements-dev.txt index aa5127b..b8acb1f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,3 @@ --e . +covdefaults>=1.2.0 coverage-enable-subprocess -pre-commit pytest diff --git a/setup.cfg b/setup.cfg index e57d130..adde60a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,52 @@ -[wheel] +[metadata] +name = setuptools_golang +version = 2.9.0 +description = A setuptools extension for building cpython extensions written in golang. +long_description = file: README.md +long_description_content_type = text/markdown +url = https://github.com/asottile/setuptools-golang +author = Anthony Sottile +author_email = asottile@umich.edu +license = MIT +license_files = LICENSE +classifiers = + License :: OSI Approved :: MIT License + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: Implementation :: PyPy + +[options] +py_modules = setuptools_golang +install_requires = + setuptools +python_requires = >=3.9 + +[options.entry_points] +console_scripts = + setuptools-golang-build-manylinux-wheels = setuptools_golang:build_manylinux_wheels +distutils.setup_keywords = + build_golang = setuptools_golang:set_build_ext + +[bdist_wheel] universal = True + +[coverage:run] +plugins = covdefaults +parallel = True +source = $PWD +data_file = $PWD/.coverage + +[mypy] +check_untyped_defs = true +disallow_any_generics = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +warn_redundant_casts = true +warn_unused_ignores = true + +[mypy-testing.*] +disallow_untyped_defs = false + +[mypy-tests.*] +disallow_untyped_defs = false diff --git a/setup.py b/setup.py index 854251e..3d93aef 100644 --- a/setup.py +++ b/setup.py @@ -1,35 +1,4 @@ -from setuptools import setup - +from __future__ import annotations -setup( - name='setuptools-golang', - description=( - 'A setuptools extension for building cpython extensions written in ' - 'golang.' - ), - url='https://github.com/asottile/setuptools-golang', - version='1.0.0', - author='Anthony Sottile', - author_email='asottile@umich.edu', - classifiers=[ - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: PyPy', - ], - py_modules=['setuptools_golang'], - install_requires=[], - entry_points={ - 'console_scripts': [ - 'setuptools-golang-build-manylinux-wheels = ' - 'setuptools_golang:build_manylinux_wheels', - ], - 'distutils.setup_keywords': [ - 'build_golang = setuptools_golang:set_build_ext', - ], - }, -) +from setuptools import setup +setup() diff --git a/setuptools_golang.py b/setuptools_golang.py index 55dbed3..e26543d 100644 --- a/setuptools_golang.py +++ b/setuptools_golang.py @@ -1,62 +1,139 @@ -from __future__ import print_function -from __future__ import unicode_literals +from __future__ import annotations +import argparse import contextlib +import copy +import errno import os -import pipes +import shlex import shutil +import stat import subprocess import sys import tempfile - +from collections.abc import Generator +from collections.abc import Sequence +from types import TracebackType +from typing import Any +from typing import Callable + +from distutils.ccompiler import CCompiler +from distutils.dist import Distribution +from setuptools import Extension from setuptools.command.build_ext import build_ext as _build_ext -def _get_cflags(compiler): - return ' '.join('-I{}'.format(p) for p in compiler.include_dirs) - - -def _check_call(cmd, cwd, env): - envparts = [ - '{}={}'.format(k, pipes.quote(v)) - for k, v in sorted(tuple(env.items())) - ] - print( - '$ {}'.format(' '.join(envparts + [pipes.quote(p) for p in cmd])), - file=sys.stderr, - ) - subprocess.check_call(cmd, cwd=cwd, env=dict(os.environ, **env)) +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[BaseException], BaseException, TracebackType], + ) -> None: + excvalue = exc[1] + if ( + isinstance(excvalue, OSError) and + 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(): +def _tmpdir() -> Generator[str]: tempdir = tempfile.mkdtemp() try: yield tempdir finally: - shutil.rmtree(tempdir) - + rmtree(tempdir) + + +def _get_cflags( + compiler: CCompiler, + macros: Sequence[tuple[str, str | None]], +) -> str: + args = [f'-I{p}' for p in compiler.include_dirs] + for macro_name, macro_value in macros: + if macro_value is None: + args.append(f'-D{macro_name}') + else: + args.append(f'-D{macro_name}={macro_value}') + return ' '.join(args) + + +LFLAG_CLANG = '-Wl,-undefined,dynamic_lookup' +LFLAG_GCC = '-Wl,--unresolved-symbols=ignore-all' +LFLAGS = (LFLAG_CLANG, LFLAG_GCC) + + +def _get_ldflags() -> str: + """Determine the correct link flags. This attempts compiles similar + to how autotools does feature detection. + """ + # windows gcc does not support linking with unresolved symbols + if sys.platform == 'win32': # pragma: win32 cover + libs = os.path.join(sys.base_prefix, 'libs') + return f'-L{libs} -lpython{sys.version_info[0]}' + else: # pragma: win32 no cover + cc = subprocess.check_output(('go', 'env', 'CC')).decode().strip() + + with _tmpdir() as tmpdir: + testf = os.path.join(tmpdir, 'test.c') + with open(testf, 'w') as f: + f.write('int f(int); int main(void) { return f(0); }\n') + + for lflag in LFLAGS: # pragma: no cover (platform specific) + try: + subprocess.check_call((cc, testf, lflag), cwd=tmpdir) + return lflag + except subprocess.CalledProcessError: + pass + else: # pragma: no cover (platform specific) + # wellp, none of them worked, fall back to gcc and they'll get + # a hopefully reasonable error message + return LFLAG_GCC + + +def _check_call(cmd: tuple[str, ...], cwd: str, env: dict[str, str]) -> None: + envparts = [f'{k}={shlex.quote(v)}' for k, v in sorted(tuple(env.items()))] + print(f'$ {" ".join(envparts)} {shlex.join(cmd)}', file=sys.stderr) + subprocess.check_call(cmd, cwd=cwd, env=dict(os.environ, **env)) -def _get_build_extension_method(base, root): - def build_extension(self, ext): - def _raise_error(msg): - raise IOError( - 'Error building extension `{}`: '.format(ext.name) + msg, - ) +def _get_build_extension_method( + base: type[_build_ext], + root: str, + strip: bool, +) -> Callable[[_build_ext, Extension], None]: + def build_extension(self: _build_ext, ext: Extension) -> None: # If there are no .go files then the parent should handle this if not any(source.endswith('.go') for source in ext.sources): - return base.build_extension(self, ext) + # the base class may mutate `self.compiler` + compiler = copy.deepcopy(self.compiler) + self.compiler, compiler = compiler, self.compiler + try: + return base.build_extension(self, ext) + finally: + self.compiler, compiler = compiler, self.compiler if len(ext.sources) != 1: - _raise_error( - 'sources must be a single file in the `main` package.\n' - 'Recieved: {!r}'.format(ext.sources) + raise OSError( + f'Error building extension `{ext.name}`: ' + f'sources must be a single file in the `main` package.\n' + f'Received: {ext.sources!r}', ) main_file, = ext.sources if not os.path.exists(main_file): - _raise_error('{} does not exist'.format(main_file)) + raise OSError( + f'Error building extension `{ext.name}`: ' + f'{main_file} does not exist', + ) main_dir = os.path.dirname(main_file) # Copy the package into a temporary GOPATH environment @@ -64,75 +141,100 @@ def _raise_error(msg): root_path = os.path.join(tempdir, 'src', root) # Make everything but the last directory (copytree interface) os.makedirs(os.path.dirname(root_path)) - shutil.copytree('.', root_path) + shutil.copytree('.', root_path, symlinks=True) pkg_path = os.path.join(root_path, main_dir) - env = {'GOPATH': tempdir} + gopath = os.environ.get('SETUPTOOLS_GOLANG_GOPATH', tempdir) + env = {'GOPATH': gopath} cmd_get = ('go', 'get', '-d') _check_call(cmd_get, cwd=pkg_path, env=env) env.update({ - 'CGO_CFLAGS': _get_cflags(self.compiler), - 'CGO_LDFLAGS': '-Wl,--unresolved-symbols=ignore-all', + 'CGO_CFLAGS': _get_cflags( + self.compiler, ext.define_macros or (), + ), + 'CGO_LDFLAGS': _get_ldflags(), }) - cmd_build = ( + + cmd_build: tuple[str, ...] = ( 'go', 'build', '-buildmode=c-shared', '-o', os.path.abspath(self.get_ext_fullpath(ext.name)), ) + # "-s" omits the symbol table and debug information + # "-w" omits DWARF debugging information + if strip: + cmd_build = (*cmd_build, '-ldflags=-s -w') + _check_call(cmd_build, cwd=pkg_path, env=env) return build_extension -def _get_build_ext_cls(base, root): - class build_ext(base): - build_extension = _get_build_extension_method(base, root) - - return build_ext +def _get_build_ext_cls( + base: type[_build_ext], + root: str, + strip: bool = True, +) -> type[_build_ext]: + attrs = {'build_extension': _get_build_extension_method(base, root, strip)} + return type('build_ext', (base,), attrs) -def set_build_ext(dist, attr, value): - root = value['root'] +def set_build_ext( + dist: Distribution, + attr: str, + value: dict[str, Any], +) -> None: base = dist.cmdclass.get('build_ext', _build_ext) - dist.cmdclass['build_ext'] = _get_build_ext_cls(base, root) - - -GOLANG = 'https://storage.googleapis.com/golang/go1.7.5.linux-amd64.tar.gz' -WHEEL_ARGS = '--no-deps --wheel-dir /tmp /dist/*.tar.gz' + dist.cmdclass['build_ext'] = _get_build_ext_cls(base, **value) + + +GOLANG = 'https://storage.googleapis.com/golang/go{}.linux-amd64.tar.gz' +SCRIPT = '''\ +cd /tmp +curl {golang} --silent --location | tar -xz +export PATH="/tmp/go/bin:$PATH" HOME=/tmp +for py in {pythons}; do + "/opt/python/$py/bin/pip" wheel --no-deps --wheel-dir /tmp /dist/*.tar.gz +done +ls *.whl | xargs -n1 --verbose auditwheel repair --wheel-dir /dist +ls -al /dist +''' + + +def build_manylinux_wheels( + argv: Sequence[str] | None = None, +) -> int: # pragma: no cover + parser = argparse.ArgumentParser() + parser.add_argument( + '--golang', default='1.17.1', + help='Override golang version (default %(default)s)', + ) + parser.add_argument( + '--pythons', default='cp37-cp37m', + help='Override pythons to build (default %(default)s)', + ) + args = parser.parse_args(argv) + golang = GOLANG.format(args.golang) + pythons = ' '.join(args.pythons.split(',')) -def build_manylinux_wheels(argv=None): # pragma: no cover assert os.path.exists('setup.py') - shutil.rmtree('dist') + if os.path.exists('dist'): + shutil.rmtree('dist') os.makedirs('dist') _check_call(('python', 'setup.py', 'sdist'), cwd='.', env={}) _check_call( ( - 'docker', 'run', - '--volume', '{}:/dist:rw'.format(os.path.abspath('dist')), - # I'd use --user, but this breaks git: - # http://stackoverflow.com/a/20272540/812183 - '--env', 'UID={}'.format(os.getuid()), - '--env', 'GID={}'.format(os.getgid()), + 'docker', 'run', '--rm', + '--volume', f'{os.path.abspath("dist")}:/dist:rw', + '--user', f'{os.getuid()}:{os.getgid()}', 'quay.io/pypa/manylinux1_x86_64:latest', - 'bash', '-exc', - 'cd /tmp\n' - 'wget {golang} -q --no-check-certificate -O /tmp/golang.tar.gz\n' - 'tar -xf /tmp/golang.tar.gz\n' - 'export GOROOT=/tmp/go\n' - 'export PATH="$GOROOT/bin:$PATH"\n' - '/opt/python/cp27-cp27mu/bin/pip wheel {wheel_args}\n' - '/opt/python/cp34-cp34m/bin/pip wheel {wheel_args}\n' - '/opt/python/cp35-cp35m/bin/pip wheel {wheel_args}\n' - '/opt/python/cp36-cp36m/bin/pip wheel {wheel_args}\n' - 'mkdir /tmp/whls\n' - 'ls *.whl | xargs -n1 --verbose auditwheel repair -w /tmp/whls\n' - 'cp /tmp/whls/* /dist\n' - 'chown "$UID:$GID" /dist/*\n' - 'ls /dist -al\n'.format(golang=GOLANG, wheel_args=WHEEL_ARGS), + 'bash', '-o', 'pipefail', '-euxc', + SCRIPT.format(golang=golang, pythons=pythons), ), cwd='.', env={}, ) print('*' * 79) print('Your wheels have been built into ./dist') print('*' * 79) + return 0 diff --git a/testing/dangling_symlink/dangling b/testing/dangling_symlink/dangling new file mode 120000 index 0000000..ee1f6cb --- /dev/null +++ b/testing/dangling_symlink/dangling @@ -0,0 +1 @@ +does_not_exist \ No newline at end of file diff --git a/testing/dangling_symlink/fake.go b/testing/dangling_symlink/fake.go new file mode 100644 index 0000000..38dd16d --- /dev/null +++ b/testing/dangling_symlink/fake.go @@ -0,0 +1,3 @@ +package main + +func main() {} diff --git a/testing/dangling_symlink/go.mod b/testing/dangling_symlink/go.mod new file mode 100644 index 0000000..f647b9d --- /dev/null +++ b/testing/dangling_symlink/go.mod @@ -0,0 +1,3 @@ +module github.com/asottile/fake + +go 1.16 diff --git a/testing/dangling_symlink/setup.py b/testing/dangling_symlink/setup.py new file mode 100644 index 0000000..1c6c660 --- /dev/null +++ b/testing/dangling_symlink/setup.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from setuptools import Extension +from setuptools import setup + + +setup( + name='fake', + ext_modules=[Extension('fake', ['fake.go'])], + build_golang={'root': 'github.com/asottile/fake'}, + # 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/testing/defines/go.mod b/testing/defines/go.mod new file mode 100644 index 0000000..f647b9d --- /dev/null +++ b/testing/defines/go.mod @@ -0,0 +1,3 @@ +module github.com/asottile/fake + +go 1.16 diff --git a/testing/defines/setup.py b/testing/defines/setup.py new file mode 100644 index 0000000..ae5f84b --- /dev/null +++ b/testing/defines/setup.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from setuptools import Extension +from setuptools import setup + + +setup( + name='sum', + ext_modules=[ + Extension( + 'sum', ['sum.go'], + define_macros=[('SUM_A', None), ('SUM_B', '2')], + ), + ], + build_golang={'root': 'github.com/asottile/fake'}, + # 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/testing/defines/sum.c b/testing/defines/sum.c new file mode 100644 index 0000000..ec311ec --- /dev/null +++ b/testing/defines/sum.c @@ -0,0 +1,29 @@ +#include + +/* Will come from go */ +PyObject* sum(PyObject* , PyObject*); + +/* To shim go's missing variadic function support */ +int PyArg_ParseTuple_ll(PyObject* args, long* a, long* b) { + return PyArg_ParseTuple(args, "ll", a, b); +} + +/* demo that macro_defines works */ +#if defined(SUM_A) && SUM_B >= 2 +static struct PyMethodDef methods[] = { + {"sum", (PyCFunction)sum, METH_VARARGS}, + {NULL, NULL} +}; +#endif + +static struct PyModuleDef module = { + PyModuleDef_HEAD_INIT, + "sum", + NULL, + -1, + methods +}; + +PyMODINIT_FUNC PyInit_sum(void) { + return PyModule_Create(&module); +} diff --git a/testing/defines/sum.go b/testing/defines/sum.go new file mode 100644 index 0000000..9fdd006 --- /dev/null +++ b/testing/defines/sum.go @@ -0,0 +1,17 @@ +package main + +// #include +// int PyArg_ParseTuple_ll(PyObject*, long*, long*); +import "C" + +//export sum +func sum(self *C.PyObject, args *C.PyObject) *C.PyObject { + var a C.long + var b C.long + if C.PyArg_ParseTuple_ll(args, &a, &b) == 0 { + return nil + } + return C.PyLong_FromLong(a + b) +} + +func main() {} 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..b901d17 --- /dev/null +++ b/testing/gomodules/reversemsg_support.go @@ -0,0 +1,23 @@ +package main + +// #include +// +// PyObject* reversemsg(); +// +// static struct PyMethodDef methods[] = { +// {"reversemsg", (PyCFunction)reversemsg, METH_NOARGS}, +// {NULL, NULL} +// }; +// +// static struct PyModuleDef module = { +// PyModuleDef_HEAD_INIT, +// "gomodules", +// NULL, +// -1, +// methods +// }; +// +// PyMODINIT_FUNC PyInit_gomodules(void) { +// return PyModule_Create(&module); +// } +import "C" diff --git a/testing/gomodules/setup.py b/testing/gomodules/setup.py new file mode 100644 index 0000000..66fed65 --- /dev/null +++ b/testing/gomodules/setup.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +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/testing/imports_gh/go.mod b/testing/imports_gh/go.mod new file mode 100644 index 0000000..f647b9d --- /dev/null +++ b/testing/imports_gh/go.mod @@ -0,0 +1,3 @@ +module github.com/asottile/fake + +go 1.16 diff --git a/testing/imports_gh/red.c b/testing/imports_gh/red.c index 7660ed9..59fa186 100644 --- a/testing/imports_gh/red.c +++ b/testing/imports_gh/red.c @@ -1,11 +1,5 @@ #include -#if PY_MAJOR_VERSION >= 3 -#define PyRed_Bytes_AS_STRING PyBytes_AS_STRING -#else -#define PyRed_Bytes_AS_STRING PyString_AS_STRING -#endif - /* Will come from go */ PyObject* red(PyObject*); @@ -20,7 +14,7 @@ void PyRed_DECREF(PyObject* obj) { } const char* PyRed_Bytes_AsString(PyObject* s) { - return PyRed_Bytes_AS_STRING(s); + return PyBytes_AS_STRING(s); } static struct PyMethodDef methods[] = { @@ -28,7 +22,6 @@ static struct PyMethodDef methods[] = { {NULL, NULL} }; -#if PY_MAJOR_VERSION >= 3 static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "red", @@ -40,8 +33,3 @@ static struct PyModuleDef module = { PyMODINIT_FUNC PyInit_red(void) { return PyModule_Create(&module); } -#else -PyMODINIT_FUNC initred(void) { - Py_InitModule3("red", methods, NULL); -} -#endif diff --git a/testing/imports_gh/setup.py b/testing/imports_gh/setup.py index 6e98f79..b9fcc1e 100644 --- a/testing/imports_gh/setup.py +++ b/testing/imports_gh/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from setuptools import Extension from setuptools import setup diff --git a/testing/internal_imports/go.mod b/testing/internal_imports/go.mod new file mode 100644 index 0000000..f647b9d --- /dev/null +++ b/testing/internal_imports/go.mod @@ -0,0 +1,3 @@ +module github.com/asottile/fake + +go 1.16 diff --git a/testing/internal_imports/hello_lib/hello_lib.c b/testing/internal_imports/hello_lib/hello_lib.c index 45c9e4f..d65e23c 100644 --- a/testing/internal_imports/hello_lib/hello_lib.c +++ b/testing/internal_imports/hello_lib/hello_lib.c @@ -1,11 +1,5 @@ #include -#if PY_MAJOR_VERSION >= 3 -#define PyHelloLib_Bytes_AS_STRING PyBytes_AS_STRING -#else -#define PyHelloLib_Bytes_AS_STRING PyString_AS_STRING -#endif - /* Will come from go */ PyObject* ohai(PyObject*); @@ -20,7 +14,7 @@ void PyHelloLib_DECREF(PyObject* obj) { } const char* PyHelloLib_Bytes_AsString(PyObject* s) { - return PyHelloLib_Bytes_AS_STRING(s); + return PyBytes_AS_STRING(s); } static struct PyMethodDef methods[] = { @@ -28,7 +22,6 @@ static struct PyMethodDef methods[] = { {NULL, NULL} }; -#if PY_MAJOR_VERSION >= 3 static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "hello_lib", @@ -40,8 +33,3 @@ static struct PyModuleDef module = { PyMODINIT_FUNC PyInit_hello_lib(void) { return PyModule_Create(&module); } -#else -PyMODINIT_FUNC inithello_lib(void) { - Py_InitModule3("hello_lib", methods, NULL); -} -#endif diff --git a/testing/internal_imports/setup.py b/testing/internal_imports/setup.py index 8fd2741..f3d284e 100644 --- a/testing/internal_imports/setup.py +++ b/testing/internal_imports/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from setuptools import Extension from setuptools import setup diff --git a/testing/multidir/dir2/sum_support.go b/testing/multidir/dir2/sum_support.go index 085d174..4be980c 100644 --- a/testing/multidir/dir2/sum_support.go +++ b/testing/multidir/dir2/sum_support.go @@ -13,7 +13,6 @@ package main // {NULL, NULL} // }; // -// #if PY_MAJOR_VERSION >= 3 // static struct PyModuleDef module = { // PyModuleDef_HEAD_INIT, // "sum_pure_go", @@ -25,9 +24,4 @@ package main // PyMODINIT_FUNC PyInit_sum_pure_go(void) { // return PyModule_Create(&module); // } -// #else -// PyMODINIT_FUNC initsum_pure_go(void) { -// Py_InitModule3("sum_pure_go", methods, NULL); -// } -// #endif import "C" diff --git a/testing/multidir/go.mod b/testing/multidir/go.mod new file mode 100644 index 0000000..f647b9d --- /dev/null +++ b/testing/multidir/go.mod @@ -0,0 +1,3 @@ +module github.com/asottile/fake + +go 1.16 diff --git a/testing/multidir/setup.py b/testing/multidir/setup.py index 1b77eca..f2af1a9 100644 --- a/testing/multidir/setup.py +++ b/testing/multidir/setup.py @@ -1,12 +1,16 @@ +from __future__ import annotations + from setuptools import Extension from setuptools import setup setup( name='multidir', - ext_modules=[Extension( - 'multidir', ['dir1/sum.go', 'dir2/sum_support.go'], - )], + ext_modules=[ + Extension( + 'multidir', ['dir1/sum.go', 'dir2/sum_support.go'], + ), + ], build_golang={'root': 'github.com/asottile/fake'}, # Would do this, but we're testing *our* implementation and this would # install from pypi. We can rely on setuptools-golang being already diff --git a/testing/notfound/go.mod b/testing/notfound/go.mod new file mode 100644 index 0000000..f647b9d --- /dev/null +++ b/testing/notfound/go.mod @@ -0,0 +1,3 @@ +module github.com/asottile/fake + +go 1.16 diff --git a/testing/notfound/setup.py b/testing/notfound/setup.py index 2e47064..3cab498 100644 --- a/testing/notfound/setup.py +++ b/testing/notfound/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from setuptools import Extension from setuptools import setup diff --git a/testing/project_with_c/go.mod b/testing/project_with_c/go.mod new file mode 100644 index 0000000..f647b9d --- /dev/null +++ b/testing/project_with_c/go.mod @@ -0,0 +1,3 @@ +module github.com/asottile/fake + +go 1.16 diff --git a/testing/project_with_c/project_with_c.c b/testing/project_with_c/project_with_c.c index 29d4d30..e45480c 100644 --- a/testing/project_with_c/project_with_c.c +++ b/testing/project_with_c/project_with_c.c @@ -9,7 +9,6 @@ static struct PyMethodDef methods[] = { {NULL, NULL} }; -#if PY_MAJOR_VERSION >= 3 static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "project_with_c", @@ -21,8 +20,3 @@ static struct PyModuleDef module = { PyMODINIT_FUNC PyInit_project_with_c(void) { return PyModule_Create(&module); } -#else -PyMODINIT_FUNC initproject_with_c(void) { - Py_InitModule3("project_with_c", methods, NULL); -} -#endif diff --git a/testing/project_with_c/project_with_c_sum/sum.c b/testing/project_with_c/project_with_c_sum/sum.c index f7158c5..3d2a63c 100644 --- a/testing/project_with_c/project_with_c_sum/sum.c +++ b/testing/project_with_c/project_with_c_sum/sum.c @@ -13,7 +13,6 @@ static struct PyMethodDef methods[] = { {NULL, NULL} }; -#if PY_MAJOR_VERSION >= 3 static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "project_with_c_sum.sum", @@ -25,8 +24,3 @@ static struct PyModuleDef module = { PyMODINIT_FUNC PyInit_sum(void) { return PyModule_Create(&module); } -#else -PyMODINIT_FUNC initsum(void) { - Py_InitModule3("project_with_c_sum.sum", methods, NULL); -} -#endif diff --git a/testing/project_with_c/setup.py b/testing/project_with_c/setup.py index 8edff81..a604c61 100644 --- a/testing/project_with_c/setup.py +++ b/testing/project_with_c/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from setuptools import Extension from setuptools import find_packages from setuptools import setup diff --git a/testing/sum/go.mod b/testing/sum/go.mod new file mode 100644 index 0000000..f647b9d --- /dev/null +++ b/testing/sum/go.mod @@ -0,0 +1,3 @@ +module github.com/asottile/fake + +go 1.16 diff --git a/testing/sum/setup.py b/testing/sum/setup.py index 33629ca..e0fa2f1 100644 --- a/testing/sum/setup.py +++ b/testing/sum/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from setuptools import Extension from setuptools import setup diff --git a/testing/sum/sum.c b/testing/sum/sum.c index 6df94ba..56cce5e 100644 --- a/testing/sum/sum.c +++ b/testing/sum/sum.c @@ -13,7 +13,6 @@ static struct PyMethodDef methods[] = { {NULL, NULL} }; -#if PY_MAJOR_VERSION >= 3 static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "sum", @@ -25,8 +24,3 @@ static struct PyModuleDef module = { PyMODINIT_FUNC PyInit_sum(void) { return PyModule_Create(&module); } -#else -PyMODINIT_FUNC initsum(void) { - Py_InitModule3("sum", methods, NULL); -} -#endif diff --git a/testing/sum_pure_go/go.mod b/testing/sum_pure_go/go.mod new file mode 100644 index 0000000..f647b9d --- /dev/null +++ b/testing/sum_pure_go/go.mod @@ -0,0 +1,3 @@ +module github.com/asottile/fake + +go 1.16 diff --git a/testing/sum_pure_go/setup.py b/testing/sum_pure_go/setup.py index 776b227..4f261f9 100644 --- a/testing/sum_pure_go/setup.py +++ b/testing/sum_pure_go/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from setuptools import Extension from setuptools import setup diff --git a/testing/sum_pure_go/sum_support.go b/testing/sum_pure_go/sum_support.go index 085d174..4be980c 100644 --- a/testing/sum_pure_go/sum_support.go +++ b/testing/sum_pure_go/sum_support.go @@ -13,7 +13,6 @@ package main // {NULL, NULL} // }; // -// #if PY_MAJOR_VERSION >= 3 // static struct PyModuleDef module = { // PyModuleDef_HEAD_INIT, // "sum_pure_go", @@ -25,9 +24,4 @@ package main // PyMODINIT_FUNC PyInit_sum_pure_go(void) { // return PyModule_Create(&module); // } -// #else -// PyMODINIT_FUNC initsum_pure_go(void) { -// Py_InitModule3("sum_pure_go", methods, NULL); -// } -// #endif import "C" diff --git a/testing/sum_sub_package/go.mod b/testing/sum_sub_package/go.mod new file mode 100644 index 0000000..f647b9d --- /dev/null +++ b/testing/sum_sub_package/go.mod @@ -0,0 +1,3 @@ +module github.com/asottile/fake + +go 1.16 diff --git a/testing/sum_sub_package/setup.py b/testing/sum_sub_package/setup.py index 42c77a2..6114e80 100644 --- a/testing/sum_sub_package/setup.py +++ b/testing/sum_sub_package/setup.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from setuptools import Extension from setuptools import find_packages from setuptools import setup @@ -7,7 +9,7 @@ name='sum_sub_package', ext_modules=[Extension('sum_sub_package.sum', ['sum_sub_package/sum.go'])], packages=find_packages(), - build_golang={'root': 'github.com/asottile/fake'}, + build_golang={'root': 'github.com/asottile/fake', 'strip': False}, # 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. diff --git a/testing/sum_sub_package/sum_sub_package/sum.c b/testing/sum_sub_package/sum_sub_package/sum.c index c1e4ca6..e47af2b 100644 --- a/testing/sum_sub_package/sum_sub_package/sum.c +++ b/testing/sum_sub_package/sum_sub_package/sum.c @@ -13,7 +13,6 @@ static struct PyMethodDef methods[] = { {NULL, NULL} }; -#if PY_MAJOR_VERSION >= 3 static struct PyModuleDef module = { PyModuleDef_HEAD_INIT, "sum_sub_package.sum", @@ -25,8 +24,3 @@ static struct PyModuleDef module = { PyMODINIT_FUNC PyInit_sum(void) { return PyModule_Create(&module); } -#else -PyMODINIT_FUNC initsum(void) { - Py_InitModule3("sum_sub_package.sum", methods, NULL); -} -#endif diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/setuptools_golang_test.py b/tests/setuptools_golang_test.py similarity index 54% rename from setuptools_golang_test.py rename to tests/setuptools_golang_test.py index 5e8dbe2..81fdaaa 100644 --- a/setuptools_golang_test.py +++ b/tests/setuptools_golang_test.py @@ -1,7 +1,8 @@ -from __future__ import unicode_literals +from __future__ import annotations import collections import os +import shlex import subprocess import sys @@ -13,9 +14,8 @@ @pytest.fixture(autouse=True, scope='session') def enable_coverage_subprocesses(): - here = os.path.dirname(os.path.abspath(__file__)) - os.environ['TOP'] = here - os.environ['COVERAGE_PROCESS_START'] = os.path.join(here, '.coveragerc') + here = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + os.environ['COVERAGE_PROCESS_START'] = os.path.join(here, 'setup.cfg') def auto_namedtuple(**kwargs): @@ -26,14 +26,13 @@ def run(*cmd, **kwargs): returncode = kwargs.pop('returncode', 0) proc = subprocess.Popen(cmd, **kwargs) out, err = proc.communicate() - out = out.decode('UTF-8') if out is not None else None - err = err.decode('UTF-8') if err is not None else None + out = out.decode('UTF-8').replace('\r', '') if out is not None else None + err = err.decode('UTF-8').replace('\r', '') if err is not None else None if returncode is not None: if proc.returncode != returncode: raise AssertionError( - '{!r} returned {} (expected {})\nout:\n{}\nerr:\n{}\n'.format( - cmd, proc.returncode, returncode, out, err, - ) + f'{cmd!r} returned {proc.returncode} (expected {returncode})\n' + f'out:\n{out}\nerr:\n{err}\n', ) return auto_namedtuple(returncode=proc.returncode, out=out, err=err) @@ -51,18 +50,21 @@ def test_sets_cmdclass(): assert dist.cmdclass['build_ext'] -@pytest.yield_fixture(scope='session') +@pytest.fixture(scope='session') def venv(tmpdir_factory): """A shared virtualenv fixture, be careful not to install two of the same package into this -- or sadness... """ + bin = 'Scripts' if sys.platform == 'win32' else 'bin' venv = tmpdir_factory.mktemp('venv').join('venv') - pip = venv.join('bin/pip').strpath - python = venv.join('bin/python').strpath + pip = venv.join(bin, 'pip').strpath + python = venv.join(bin, 'python').strpath # Make sure this virtualenv has the same executable run('virtualenv', venv.strpath, '-p', sys.executable) # Install this so we can get coverage - run(pip, 'install', 'coverage-enable-subprocess') + run(pip, 'install', 'covdefaults>=1.2.0', 'coverage-enable-subprocess') + # Install wheel as well so we can non-pep517 + run(pip, 'install', 'wheel') # Install us! run(pip, 'install', '-e', '.') yield auto_namedtuple(venv=venv, pip=pip, python=python) @@ -74,9 +76,9 @@ def venv(tmpdir_factory): @pytest.mark.parametrize( ('pkg', 'mod'), ( - ('testing/sum', 'sum'), - ('testing/sum_pure_go', 'sum_pure_go'), - ('testing/sum_sub_package', 'sum_sub_package.sum'), + (os.path.join('testing', 'sum'), 'sum'), + (os.path.join('testing', 'sum_pure_go'), 'sum_pure_go'), + (os.path.join('testing', 'sum_sub_package'), 'sum_sub_package.sum'), ), ) def test_sum_integration(venv, pkg, mod): @@ -90,7 +92,8 @@ def test_sum_integration(venv, pkg, mod): def test_integration_project_with_c(venv): test_sum_integration( - venv, 'testing/project_with_c', 'project_with_c_sum.sum', + venv, + os.path.join('testing', 'project_with_c'), 'project_with_c_sum.sum', ) out = run_output(venv.python, '-c', HELLO_WORLD) assert out == 'hello world\n' @@ -100,14 +103,23 @@ def test_integration_project_with_c(venv): def test_integration_imports_gh(venv): - run(venv.pip, 'install', 'testing/imports_gh') + run(venv.pip, 'install', os.path.join('testing', 'imports_gh')) out = run_output(venv.python, '-c', RED) - assert out == '\x1b[31mohai\x1b[0m\n' + 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', 'testing/notfound', + venv.pip, 'install', os.path.join('testing', 'notfound'), returncode=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) assert ret.returncode != 0 @@ -119,7 +131,7 @@ def test_integration_notfound(venv): def test_integration_multidir(venv): ret = run( - venv.pip, 'install', 'testing/multidir', + venv.pip, 'install', os.path.join('testing', 'multidir'), returncode=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) assert ret.returncode != 0 @@ -133,6 +145,32 @@ def test_integration_multidir(venv): def test_integration_internal_imports(venv): - run(venv.pip, 'install', 'testing/internal_imports') + run(venv.pip, 'install', os.path.join('testing', 'internal_imports')) out = run_output(venv.python, '-c', OHAI) assert out == 'ohai, Anthony\n' + + +def test_integration_user_gopath(venv, tmpdir): + testdir = os.path.join('testing', 'gomodules') + + gopath = str(tmpdir.join('gopath')) + env = {**os.environ, 'SETUPTOOLS_GOLANG_GOPATH': gopath} + ret = run( + venv.pip, 'install', '-v', testdir, + env=env, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + + assert f'$ GOPATH={shlex.quote(gopath)} go get -d' in ret.out + + +def test_integration_defines(venv): + run(venv.pip, 'install', os.path.join('testing', 'defines')) + out = run_output(venv.python, '-c', SUM.format('sum')) + assert out == '3\n' + + +def test_regression_dangling_symlink(venv): + # this raises an error because of a dangling symlink + run(venv.pip, 'install', os.path.join('testing', 'dangling_symlink')) diff --git a/tox.ini b/tox.ini index 1d3428e..4e4a4dc 100644 --- a/tox.ini +++ b/tox.ini @@ -1,23 +1,20 @@ [tox] -project = setuptools-golang -# These should match the travis env list -envlist = py27,py35,py36,pypy +envlist = py,pre-commit [testenv] deps = -rrequirements-dev.txt -passenv = HOME GOROOT -setenv = TOP={toxinidir} +passenv = * +setenv = PWD={toxinidir} commands = coverage erase - coverage run -m pytest {posargs:setuptools_golang_test.py} + coverage run -m pytest {posargs:tests} coverage combine - coverage report --show-missing --fail-under 100 - pre-commit install -f --install-hooks - pre-commit run --all-files + coverage report -[testenv:venv] -envdir = venv-{[tox]project} -commands = +[testenv:pre-commit] +skip_install = true +deps = pre-commit +commands = pre-commit run --all-files --show-diff-on-failure [pep8] -ignore = E265,E309,E501 +ignore = E265,E501,W504