Skip to content
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

Add pytest_test support to rules_python. #243

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 46 additions & 0 deletions experimental/examples/pytest/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Copyright 2019 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
package(default_visibility = ["//visibility:public"])

licenses(["notice"]) # Apache 2.0

load("@examples_pytest//:requirements.bzl", "requirement")
load("//experimental/python:pytest.bzl", "pytest_test")
load("//python:defs.bzl", "py_library")

py_library(
name = "conftest",
srcs = ["conftest.py"],
)

pytest_test(
name = "all_tests",
test_files = glob(["*_test.py"]),
deps = [
":conftest",
requirement("pytest"),
# These should be pulled in by requirement("pytest"), but it's currently broken
# See #110 and #90
requirement("attrs"),
requirement("atomicwrites"),
requirement("importlib_metadata"),
requirement("more_itertools"),
requirement("packaging"),
requirement("pathlib2"),
requirement("pluggy"),
requirement("py"),
requirement("wcwidth"),
requirement("zipp"),
],
)
19 changes: 19 additions & 0 deletions experimental/examples/pytest/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2019 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import pytest

@pytest.fixture
def greeting():
return "Hello Bazel!"
16 changes: 16 additions & 0 deletions experimental/examples/pytest/other_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2019 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

def test_other():
assert 4 == 2 + 2
2 changes: 2 additions & 0 deletions experimental/examples/pytest/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Need https://github.com/pytest-dev/pytest/pull/4738 to work with Bazel.
pytest>=4.2.1
16 changes: 16 additions & 0 deletions experimental/examples/pytest/sample_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright 2019 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

def test_greeting(greeting):
assert "Bazel" in greeting
5 changes: 4 additions & 1 deletion experimental/python/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ package(default_visibility = ["//visibility:public"])

licenses(["notice"]) # Apache 2.0

exports_files(["wheel.bzl"])
exports_files([
"wheel.bzl",
"pytest.bzl",
])
107 changes: 107 additions & 0 deletions experimental/python/pytest.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Copyright 2018 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Rules for converting py.test tests into bazel targets."""

load("//python:defs.bzl", "py_test")

def _sanitize_name(filename):
return filename.replace("/", "__").replace(".", "_")

def _pytest_runner_impl(ctx):
"""Creates a wrapper script that runs py.test runner for given list of files."""
runner_script = ctx.actions.declare_file(ctx.attr.name)
ctx.actions.write(runner_script, """\
import sys
import pytest

# sys.exit to propagate the exit code to bazel
# sys.argv[1:] to pass additional flags from bazel --test_arg to py.test,
# e.g. "bazel test --test_arg=-s :my_test"
sys.exit(pytest.main(sys.argv[1:] + %s))
""" % repr(ctx.attr.test_files))
return [DefaultInfo(executable = runner_script)]

pytest_runner = rule(
implementation = _pytest_runner_impl,
attrs = {
"test_files": attr.string_list(mandatory = True, allow_empty = False),
},
doc = """Creates a wrapper script that runs py.test runner for given list of files.

This is an implementation detail for pytest_test() macro. Use pytest_test() instead.
""",
executable = True,
)

def _make_pytest_target(name, test_files, **kwargs):
"""Instantiate pytest_runner rule and a corresponding py_test rule.

Args:
name: Name of the rule
test_files: List of py.test files to be executed.
**kwargs: Additional arguments to pass to the py_test targets, e.g. deps.
"""
abs_test_files = [
native.package_name() + "/" + test_file
for test_file in test_files
]
runner_file = name + "_runner.py"
pytest_runner(
name = runner_file,
test_files = abs_test_files,
)
py_test(
name = name,
srcs = [runner_file] + test_files,
main = runner_file,
**kwargs
)

def pytest_test(name, test_files, **kwargs):
"""Create bazel native py_test rules for tests using py.test framework.

Args:
name: name of the generated test rule,
test_files: Python test files to run,
**kwargs: other arguments (e.g. "deps") are passed to py_test rule.

Make sure that "deps" include:
- py_library with pytest, e.g. created by pip_import.
- py_library target that contains conftest.py file, if you use conftest.

Example:
py_library(
name = "conftest",
srcs = ["conftest.py"],
)

pytest_test(
name = "all_test",
test_files = glob(["*_test.py"]),
deps = [
":conftest",
requirement("pytest"),
],
)
"""
if len(test_files) > 1:
for test_file in test_files:
_make_pytest_target(
name = name + "_" + _sanitize_name(test_file),
test_files = [test_file],
**kwargs
)
else:
_make_pytest_target(name, test_files, **kwargs)
4 changes: 4 additions & 0 deletions internal_deps.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ def examples():
name = "examples_extras",
requirements = "@rules_python//examples/extras:requirements.txt",
)
pip_import(
name = "examples_pytest",
requirements = "@rules_python//experimental/examples/pytest:requirements.txt",
)
5 changes: 5 additions & 0 deletions internal_setup.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ load(
"@examples_helloworld//:requirements.bzl",
_helloworld_install = "pip_install",
)
load(
"@examples_pytest//:requirements.bzl",
_pytest_install = "pip_install",
)
load(
"@examples_version//:requirements.bzl",
_version_install = "pip_install",
Expand All @@ -36,3 +40,4 @@ def rules_python_internal_setup():
_version_install()
_boto_install()
_extras_install()
_pytest_install()