pw_build: python_test_deps for Python packages

Split python_test_deps from python_deps. This makes the true
dependencies clearer and prevents circular dependencies in some cases
(pw_protobuf_compiler's tests depend on compiled protobufs).

Change-Id: I97dcba908995ef281850105d0cecafd0d87522e8
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/30561
Reviewed-by: Keir Mierle <keir@google.com>
Commit-Queue: Wyatt Hepler <hepler@google.com>
diff --git a/pw_bloat/bloat.gni b/pw_bloat/bloat.gni
index a22f0b2..432358c 100644
--- a/pw_bloat/bloat.gni
+++ b/pw_bloat/bloat.gni
@@ -183,7 +183,7 @@
           "$target_gen_dir/${target_name}.txt",
           _doc_rst_output,
         ]
-        deps = _all_target_dependencies + [ "$dir_pw_bloat/py" ]
+        deps = _all_target_dependencies
         args = _bloat_script_args + _binary_paths
 
         # Print size reports to stdout when they are generated.
diff --git a/pw_build/python.gni b/pw_build/python.gni
index 5af65c7..5bb412c 100644
--- a/pw_build/python.gni
+++ b/pw_build/python.gni
@@ -50,6 +50,7 @@
 #   sources: Python sources files in the package.
 #   tests: Test files for this Python package.
 #   python_deps: Dependencies on other pw_python_packages in the GN build.
+#   python_test_deps: Test-only pw_python_package dependencies.
 #   other_deps: Dependencies on GN targets that are not pw_python_packages.
 #   inputs: Other files to track, such as package_data.
 #   lint: If true (default), applies mypy and pylint to the package. If false,
@@ -128,6 +129,21 @@
     }
   }
 
+  # All dependencies needed for the package and its tests.
+  _python_test_deps = _python_deps
+  if (defined(invoker.python_test_deps)) {
+    foreach(test_dep, invoker.python_test_deps) {
+      _python_test_deps += [ get_label_info(test_dep, "label_no_toolchain") ]
+    }
+  }
+
+  if (_test_sources == []) {
+    assert(!defined(invoker.python_test_deps),
+           "python_test_deps was provided, but there are no tests in " +
+               get_label_info(":$target_name", "label_no_toolchain"))
+    not_needed(_python_test_deps)
+  }
+
   _internal_target = "$target_name._internal"
 
   # Create groups with the public target names ($target_name, $target_name.lint,
@@ -225,6 +241,14 @@
     ]
   }
 
+  if (_should_lint || _test_sources != []) {
+    # Packages that must be installed to use the package or run its tests.
+    _test_install_deps = []
+    foreach(dep, _python_test_deps) {
+      _test_install_deps += [ "$dep.install" ]
+    }
+  }
+
   # For packages that are not generated, create targets to run mypy and pylint.
   # Linting is not performed on generated packages.
   if (_should_lint) {
@@ -262,8 +286,9 @@
       directory = _lint_directory
       stamp = true
 
-      deps = [ ":$_internal_target.install" ]
-      foreach(dep, _python_deps) {
+      deps = _test_install_deps
+
+      foreach(dep, _python_test_deps) {
         deps += [ "$dep.lint.mypy" ]
       }
     }
@@ -293,8 +318,9 @@
       directory = _lint_directory
       stamp = "$target_gen_dir/{{source_target_relative}}.pylint.passed"
 
-      deps = [ ":$_internal_target.install" ]
-      foreach(dep, _python_deps) {
+      deps = _test_install_deps
+
+      foreach(dep, _python_test_deps) {
         deps += [ "$dep.lint.pylint" ]
       }
     }
@@ -322,8 +348,9 @@
       script = test
       stamp = true
 
-      deps = [ ":$_internal_target.install" ]
-      foreach(dep, _python_deps) {
+      deps = _test_install_deps
+
+      foreach(dep, _python_test_deps) {
         deps += [ "$dep.tests" ]
       }
     }
diff --git a/pw_hdlc/py/BUILD.gn b/pw_hdlc/py/BUILD.gn
index 255c10b..f01f29d 100644
--- a/pw_hdlc/py/BUILD.gn
+++ b/pw_hdlc/py/BUILD.gn
@@ -31,9 +31,9 @@
     "encode_test.py",
   ]
   python_deps = [
-    "$dir_pw_build/py",
     "$dir_pw_protobuf_compiler/py",
     "$dir_pw_rpc/py",
   ]
+  python_test_deps = [ "$dir_pw_build/py" ]
   pylintrc = "$dir_pigweed/.pylintrc"
 }
diff --git a/pw_protobuf/py/BUILD.gn b/pw_protobuf/py/BUILD.gn
index 6d42440..8f363da 100644
--- a/pw_protobuf/py/BUILD.gn
+++ b/pw_protobuf/py/BUILD.gn
@@ -25,9 +25,6 @@
     "pw_protobuf/plugin.py",
     "pw_protobuf/proto_tree.py",
   ]
-  python_deps = [
-    "$dir_pw_cli/py",
-    "$dir_pw_protobuf_compiler/py",
-  ]
+  python_deps = [ "$dir_pw_cli/py" ]
   pylintrc = "$dir_pigweed/.pylintrc"
 }
diff --git a/pw_protobuf/py/setup.py b/pw_protobuf/py/setup.py
index 2c8c692..d55b53b 100644
--- a/pw_protobuf/py/setup.py
+++ b/pw_protobuf/py/setup.py
@@ -31,5 +31,4 @@
         'protobuf',
         'pw_cli',
     ],
-    tests_require=['pw_protobuf_compiler'],
 )
diff --git a/pw_rpc/py/BUILD.gn b/pw_rpc/py/BUILD.gn
index 802efd2..e80ccbf 100644
--- a/pw_rpc/py/BUILD.gn
+++ b/pw_rpc/py/BUILD.gn
@@ -41,9 +41,9 @@
     "packets_test.py",
   ]
   python_deps = [
-    "$dir_pw_build/py",
     "$dir_pw_protobuf_compiler/py",
     "$dir_pw_status/py",
   ]
+  python_test_deps = [ "$dir_pw_build/py" ]
   pylintrc = "$dir_pigweed/.pylintrc"
 }