Repository: pantsbuild/pants

Issue:
Requesting `AllTargets` in a plugin fails if an entry in `[python.resolves]` points to a nonexistent file
**Describe the bug**

In one of our in-repo plugins, we request:
```
await Get(AllTargets, AllTargetsRequest())
```

This typically works fine, but if I add a new `[python.resolves]` entry:
```
[python.resolves]
new = "path/to/new.lockfile"
```

Then the goal logic explodes with:
```
Traceback (most recent call last):
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
    res = rule.send(arg) if err is None else rule.throw(throw or err)
  File "/Users/dan.moran/color/build/pants/plugins/lockfile/pex/register.py", line 304, in lock_goal
    all_targets = await Get(AllTargets, AllTargetsRequest())
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 118, in __await__
    result = yield self
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
    res = rule.send(arg) if err is None else rule.throw(throw or err)
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/graph.py", line 455, in find_all_targets
    tgts = await Get(
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 118, in __await__
    result = yield self
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
    res = rule.send(arg) if err is None else rule.throw(throw or err)
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/specs_rules.py", line 175, in addresses_from_raw_specs_without_file_owners
    target_parametrizations_list = await MultiGet(
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 358, in MultiGet
    return await _MultiGet(tuple(__arg0))
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 165, in __await__
    result = yield self.gets
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
    res = rule.send(arg) if err is None else rule.throw(throw or err)
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/graph.py", line 283, in resolve_target_parametrizations
    all_generated = await MultiGet(
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 358, in MultiGet
    return await _MultiGet(tuple(__arg0))
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 165, in __await__
    result = yield self.gets
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
    res = rule.send(arg) if err is None else rule.throw(throw or err)
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/graph.py", line 1418, in generate_file_targets
    sources_paths = await Get(
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 118, in __await__
    result = yield self
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 623, in native_engine_generator_send
    res = rule.send(arg) if err is None else rule.throw(throw or err)
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/graph.py", line 1031, in resolve_source_paths
    paths = await Get(Paths, PathGlobs, path_globs)
  File "/Users/dan.moran/Library/Caches/nce/56153e74337dc4e840948775afc9384890c3683383a5da29460a7aa1ccb965f6/bindings/venvs/2.16.0rc0+git06aaef7f/lib/python3.9/site-packages/pants/engine/internals/selectors.py", line 118, in __await__
    result = yield self
native_engine.IntrinsicError: Unmatched glob from path/to:_new_lockfile's `sources` field: "path/to/new.lockfile"
```

**Pants version**

`PANTS_SHA=06aaef7f21092a7bd76ba157cd221266b8444653` (on the 2.16.x branch)

**OS**

MacOS but would presumably see it on Linux too if I pushed my branch to CI.

**Additional info**

I believe the error is happening because the rule that provides `AllTargets` [here](https://github.com/pantsbuild/pants/blob/main/src/python/pants/engine/internals/graph.py#L588-L593) wraps around:
```
await Get(
    Targets,
    RawSpecsWithoutFileOwners(
        recursive_globs=(RecursiveGlobSpec(""),),
        description_of_origin="the `AllTargets` rule"
    ),
)
```

`RawSpecsWithoutFileOwners` has an `unmatched_glob_behavior` field [here](https://github.com/pantsbuild/pants/blob/main/src/python/pants/base/specs.py#L365) that defaults to `GlobMatchErrorBehavior.error`.



Discussion:
Hrmmm although - it looks like that `unmatched_glob_behavior` field is only used in the `RawSpecsWithoutFileOwners.to_build_file_path_globs_tuple` method. If I replace my `AllTargets` request with this:
```
all_targets = await Get(
    Targets,
    RawSpecsWithoutFileOwners(
        recursive_globs=(RecursiveGlobSpec(""),),
        description_of_origin="the `lock` goal",
        unmatched_glob_behavior=GlobMatchErrorBehavior.ignore,
    ),
)
```
I still get the `Unmatched glob` intrinsic error 🤔 
This also seems to bite `pants check` when `mypy` is enabled, because its logic includes an `await Get(AllTargets, AllTargetsRequest())`

Resolving patch:
diff --git a/src/python/pants/core/target_types.py b/src/python/pants/core/target_types.py
--- a/src/python/pants/core/target_types.py
+++ b/src/python/pants/core/target_types.py
@@ -903,8 +903,17 @@ class LockfileTarget(Target):
 
 
 class LockfilesGeneratorSourcesField(MultipleSourcesField):
+    """Sources field for synthesized `_lockfiles` targets.
+
+    It is special in that it always ignores any missing files, regardless of the global
+    `--unmatched-build-file-globs` option.
+    """
+
     help = generate_multiple_sources_field_help_message("Example: `sources=['example.lock']`")
 
+    def path_globs(self, unmatched_build_file_globs: UnmatchedBuildFileGlobs) -> PathGlobs:  # type: ignore[misc]
+        return super().path_globs(UnmatchedBuildFileGlobs.ignore())
+
 
 class LockfilesGeneratorTarget(TargetFilesGenerator):
     alias = "_lockfiles"


Test patch:
diff --git a/src/python/pants/core/target_types_test.py b/src/python/pants/core/target_types_test.py
--- a/src/python/pants/core/target_types_test.py
+++ b/src/python/pants/core/target_types_test.py
@@ -23,6 +23,7 @@
     FilesGeneratorTarget,
     FileSourceField,
     FileTarget,
+    LockfilesGeneratorSourcesField,
     LockfileSourceField,
     RelocatedFiles,
     RelocateFilesViaCodegenRequest,
@@ -448,3 +449,20 @@ def test_lockfile_glob_match_error_behavior(
             UnmatchedBuildFileGlobs(error_behavior)
         ).glob_match_error_behavior
     )
+
+
+@pytest.mark.parametrize(
+    "error_behavior", [GlobMatchErrorBehavior.warn, GlobMatchErrorBehavior.error]
+)
+def test_lockfiles_glob_match_error_behavior(
+    error_behavior: GlobMatchErrorBehavior,
+) -> None:
+    lockfile_sources = LockfilesGeneratorSourcesField(
+        ["test.lock"], Address("", target_name="lockfiles-test")
+    )
+    assert (
+        GlobMatchErrorBehavior.ignore
+        == lockfile_sources.path_globs(
+            UnmatchedBuildFileGlobs(error_behavior)
+        ).glob_match_error_behavior
+    )