-
-
Notifications
You must be signed in to change notification settings - Fork 650
Add a pytest rules #464
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 a pytest rules #464
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| load("@rules_python//python:defs.bzl", "py_binary", | ||
| "py_library", "py_test", "py_test_suite", "pytest_test") | ||
| load("@pip//:requirements.bzl", "requirement") | ||
|
|
||
| TEST_DEPS = [ | ||
| requirement("pluggy"), | ||
| requirement("py"), | ||
| requirement("pytest"), | ||
| requirement("pytest-instafail"), | ||
| requirement("pytest-trio"), | ||
| requirement("pytest-mock"), | ||
| ] | ||
|
|
||
| py_library( | ||
| name="lib", | ||
| srcs="main.py" | ||
| ) | ||
|
|
||
| py_test_suite( | ||
| name="unit", | ||
| size="small", | ||
| srcs="main_tests.py", | ||
| args=[ | ||
| "--instafail", | ||
| ], | ||
| deps=[ | ||
| ":lib", | ||
| ] + TEST_DEPS, | ||
| ) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| workspace(name="example_repo") | ||
|
|
||
| load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") | ||
|
|
||
| http_archive( | ||
| name="rules_python", | ||
| sha256="778197e26c5fbeb07ac2a2c5ae405b30f6cb7ad1f5510ea6fdac03bded96cc6f", | ||
| url="https://github.com/bazelbuild/rules_python/releases/download/0.2.0/rules_python-0.2.0.tar.gz", | ||
| ) | ||
|
|
||
| load("@rules_python//python:pip.bzl", "pip_install", "pip_repositories") | ||
|
|
||
| pip_install( | ||
| requirements="//:requirements.txt", | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| def add(first, second): | ||
| return first + second |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import pytest | ||
|
|
||
| from .main import add | ||
|
|
||
|
|
||
| @pytest.mark.parameterize("first,second,expected", [1, 2, 3]) | ||
| def test_add(first, second, expected): | ||
| result = add(first, second) | ||
| assert result == expected |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| load("@rules_python//python:defs.bzl", "PyInfo", "py_test") | ||
|
|
||
| def _stringify(paths): | ||
| return repr(paths) | ||
|
|
||
| def _pytest_runner_impl(ctx): | ||
| if len(ctx.attr.srcs) == 0: | ||
| fail("No test files specified.") | ||
|
|
||
| expanded_args = [ctx.expand_location(arg, ctx.attr.data) for arg in ctx.attr.args] | ||
|
|
||
| runner = ctx.actions.declare_file(ctx.attr.name) | ||
| ctx.actions.write( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be a template substitution action. |
||
| runner, | ||
| """ | ||
| if __name__ == "__main__": | ||
| import sys | ||
| import pytest | ||
|
|
||
| args = ["-ra"] + %s + sys.argv[1:] + %s | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was going to request --long-args for readability but seems it's just |
||
|
|
||
| sys.exit(pytest.main(args))""" % (_stringify(expanded_args), _stringify([src.path for src in ctx.files.srcs])), | ||
| is_executable = True, | ||
| ) | ||
|
|
||
| return [ | ||
| DefaultInfo( | ||
| files = depset([runner]), | ||
| runfiles = ctx.runfiles(ctx.files.data), | ||
| executable = runner, | ||
| ), | ||
| ] | ||
|
|
||
| _pytest_runner = rule( | ||
| _pytest_runner_impl, | ||
| attrs = { | ||
| "srcs": attr.label_list( | ||
| allow_files = [".py"], | ||
| ), | ||
| "deps": attr.label_list( | ||
| providers = [ | ||
| PyInfo, | ||
| ], | ||
| ), | ||
| "args": attr.string_list( | ||
| default = [], | ||
| ), | ||
| "data": attr.label_list( | ||
| allow_empty = True, | ||
| allow_files = True, | ||
| ), | ||
| "python_version": attr.string( | ||
| values = ["PY2", "PY3"], | ||
| default = "PY3", | ||
| ), | ||
| }, | ||
| ) | ||
|
|
||
| def pytest_test(name, srcs, deps = None, args = None, data = None, python_version = None, **kwargs): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| runner_target = "%s-runner.py" % name | ||
|
|
||
| _pytest_runner( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What necessitates the creation of a separate executable? Can we just have a special |
||
| name = runner_target, | ||
| testonly = True, | ||
| srcs = srcs, | ||
| deps = deps, | ||
| args = args, | ||
| data = data, | ||
| python_version = python_version, | ||
| ) | ||
|
|
||
| py_test( | ||
| name = name, | ||
| python_version = python_version, | ||
| srcs = srcs + [runner_target], | ||
| deps = deps, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Think that users of the any Is there an problem with that approach I'm not seeing?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only issue I can think of involves which version of pytest to use. Presumably users need to have it in their requirements.txt, or we inject a default version if not. We should definitely add Other related issues include user provided plugin version eg. pytest_coverage which need to resolve with pytest, and how / when to add those deps to the py_pytest_test rule. |
||
| main = runner_target, | ||
| legacy_create_init = False, | ||
| imports = ["."], | ||
| **kwargs | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # load("@rules_python//python:defs.bzl", "py_library") | ||
| load(":pytest.bzl", "pytest_test") | ||
|
|
||
| def _is_test(file): | ||
| return file.startswith("test_") or file.endswith("_tests.py") | ||
|
|
||
| def py_test_suite(name, srcs, size = None, deps = None, python_version = None, imports = None, visibility = None, **kwargs): | ||
| library_name = "%s-test-lib" % name | ||
|
|
||
| py_library( | ||
| name = library_name, | ||
| testonly = True, | ||
| srcs = srcs, | ||
| deps = deps, | ||
| imports = imports, | ||
| ) | ||
|
|
||
| tests = [] | ||
| for src in srcs: | ||
| if _is_test(src): | ||
| test_name = "%s-%s" % (name, src) | ||
|
|
||
| tests.append(test_name) | ||
|
|
||
| pytest_test( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't think we should have the 'generic |
||
| name = test_name, | ||
| size = size, | ||
| srcs = [src], | ||
| deps = [library_name], | ||
| python_version = python_version, | ||
| **kwargs | ||
| ) | ||
| native.test_suite( | ||
| name = name, | ||
| tests = tests, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
?? That's unexpected behaviour to me. I would have thought an empty list would do nothing or error. Do you have experience with |
||
| visibility = visibility, | ||
| ) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where are these deps coming from? The relevant requirements.txt file is empty in the PR.