def assert_injected( rule_runner: RuleRunner, *, source_roots: List[str], original_declared_files: List[str], original_undeclared_files: List[str], expected_discovered: List[str], ) -> None: rule_runner.set_options([f"--source-root-patterns={source_roots}"]) for f in original_undeclared_files: rule_runner.create_file(f, "# undeclared") request = AncestorFilesRequest( "__init__.py", rule_runner.make_snapshot({fp: "# declared" for fp in original_declared_files}), ) result = rule_runner.request(AncestorFiles, [request]).snapshot assert list(result.files) == sorted(expected_discovered) materialized_result = rule_runner.request(DigestContents, [result.digest]) for file_content in materialized_result: path = file_content.path if not path.endswith("__init__.py"): continue assert path in original_declared_files or path in expected_discovered expected = b"# declared" if path in original_declared_files else b"# undeclared" assert file_content.content == expected
async def infer_python_init_dependencies( request: InferInitDependencies, python_infer_subsystem: PythonInferSubsystem) -> InferredDependencies: if not python_infer_subsystem.inits: return InferredDependencies([]) fp = request.sources_field.file_path assert fp is not None init_files = await Get( AncestorFiles, AncestorFilesRequest(input_files=(fp, ), requested=("__init__.py", "__init__.pyi")), ) owners = await MultiGet( Get(Owners, OwnersRequest((f, ))) for f in init_files.snapshot.files) return InferredDependencies(itertools.chain.from_iterable(owners))
async def infer_python_init_dependencies( request: InferInitDependencies, python_infer_subsystem: PythonInferSubsystem, python_setup: PythonSetup, ) -> InferredDependencies: if ( not python_infer_subsystem.options.is_default("inits") and not python_infer_subsystem.inits ) or python_infer_subsystem.init_files is InitFilesInference.never: return InferredDependencies([]) ignore_empty_files = ( python_infer_subsystem.options.is_default("inits") and python_infer_subsystem.init_files is InitFilesInference.content_only ) fp = request.sources_field.file_path assert fp is not None init_files = await Get( AncestorFiles, AncestorFilesRequest( input_files=(fp,), requested=("__init__.py", "__init__.pyi"), ignore_empty_files=ignore_empty_files, ), ) owners = await MultiGet(Get(Owners, OwnersRequest((f,))) for f in init_files.snapshot.files) original_tgt, owner_tgts = await MultiGet( Get( WrappedTarget, WrappedTargetRequest( request.sources_field.address, description_of_origin="<infallible>" ), ), Get(Targets, Addresses(itertools.chain.from_iterable(owners))), ) resolve = original_tgt.target[PythonResolveField].normalized_value(python_setup) python_owners = [ tgt.address for tgt in owner_tgts if ( tgt.has_field(PythonSourceField) and tgt[PythonResolveField].normalized_value(python_setup) == resolve ) ] return InferredDependencies(python_owners)
async def infer_python_conftest_dependencies( request: InferConftestDependencies, python_infer_subsystem: PythonInferSubsystem, ) -> InferredDependencies: if not python_infer_subsystem.conftests: return InferredDependencies([]) fp = request.sources_field.file_path assert fp is not None conftest_files = await Get( AncestorFiles, AncestorFilesRequest(input_files=(fp, ), requested=("conftest.py", )), ) owners = await MultiGet( # NB: Because conftest.py files effectively always have content, we require an # owning target. Get(Owners, OwnersRequest((f, ), OwnersNotFoundBehavior.error)) for f in conftest_files.snapshot.files) return InferredDependencies(itertools.chain.from_iterable(owners))
async def infer_python_conftest_dependencies( request: InferConftestDependencies, python_infer_subsystem: PythonInferSubsystem, ) -> InferredDependencies: if not python_infer_subsystem.conftests: return InferredDependencies([]) # Locate conftest.py files not already in the Snapshot. hydrated_sources = await Get(HydratedSources, HydrateSourcesRequest(request.sources_field)) extra_conftest_files = await Get( AncestorFiles, AncestorFilesRequest("conftest.py", hydrated_sources.snapshot), ) # And add dependencies on their owners. # NB: Because conftest.py files effectively always have content, we require an owning target. owners = await MultiGet( Get(Owners, OwnersRequest((f, ), OwnersNotFoundBehavior.error)) for f in extra_conftest_files.snapshot.files) return InferredDependencies(itertools.chain.from_iterable(owners))
async def infer_python_conftest_dependencies( request: InferConftestDependencies, python_infer_subsystem: PythonInferSubsystem, python_setup: PythonSetup, ) -> InferredDependencies: if not python_infer_subsystem.conftests: return InferredDependencies([]) fp = request.sources_field.file_path assert fp is not None conftest_files = await Get( AncestorFiles, AncestorFilesRequest(input_files=(fp,), requested=("conftest.py",)), ) owners = await MultiGet( # NB: Because conftest.py files effectively always have content, we require an # owning target. Get(Owners, OwnersRequest((f,), OwnersNotFoundBehavior.error)) for f in conftest_files.snapshot.files ) original_tgt, owner_tgts = await MultiGet( Get( WrappedTarget, WrappedTargetRequest( request.sources_field.address, description_of_origin="<infallible>" ), ), Get(Targets, Addresses(itertools.chain.from_iterable(owners))), ) resolve = original_tgt.target[PythonResolveField].normalized_value(python_setup) python_owners = [ tgt.address for tgt in owner_tgts if ( tgt.has_field(PythonSourceField) and tgt[PythonResolveField].normalized_value(python_setup) == resolve ) ] return InferredDependencies(python_owners)
async def infer_python_init_dependencies( request: InferInitDependencies, python_infer_subsystem: PythonInferSubsystem) -> InferredDependencies: if not python_infer_subsystem.inits: return InferredDependencies([]) # Locate __init__.py files not already in the Snapshot. hydrated_sources = await Get(HydratedSources, HydrateSourcesRequest(request.sources_field)) extra_init_files = await Get( AncestorFiles, AncestorFilesRequest("__init__.py", hydrated_sources.snapshot), ) # And add dependencies on their owners. # NB: Because the python_sources rules always locate __init__.py files, and will trigger an # error for files that have content but have not already been included via a dependency, we # don't need to error for unowned files here. owners = await MultiGet( Get(Owners, OwnersRequest((f, ))) for f in extra_init_files.snapshot.files) return InferredDependencies(itertools.chain.from_iterable(owners))
def assert_injected( rule_runner: RuleRunner, *, input_files: list[str], empty_files: list[str], nonempty_files: list[str], expected_discovered: list[str], ignore_empty_files: bool, ) -> None: rule_runner.write_files({ **{f: "" for f in empty_files}, **{f: "foo" for f in nonempty_files} }) request = AncestorFilesRequest( requested=("__init__.py", ), input_files=tuple(input_files), ignore_empty_files=ignore_empty_files, ) result = rule_runner.request(AncestorFiles, [request]).snapshot assert list(result.files) == sorted(expected_discovered)
async def prepare_python_sources( request: PythonSourceFilesRequest, union_membership: UnionMembership) -> PythonSourceFiles: sources = await Get( SourceFiles, SourceFilesRequest( (tgt.get(Sources) for tgt in request.targets), for_sources_types=request.valid_sources_types, enable_codegen=True, ), ) missing_init_files = await Get( AncestorFiles, AncestorFilesRequest("__init__.py", sources.snapshot), ) init_injected = await Get( Snapshot, MergeDigests( (sources.snapshot.digest, missing_init_files.snapshot.digest)), ) source_root_objs = await MultiGet( Get(SourceRoot, SourceRootRequest, SourceRootRequest.for_target(tgt)) for tgt in request.targets if (tgt.has_field(PythonSources) or tgt.has_field(ResourcesSources) or tgt.get(Sources).can_generate(PythonSources, union_membership) or tgt.get(Sources).can_generate(ResourcesSources, union_membership))) source_root_paths = { source_root_obj.path for source_root_obj in source_root_objs } return PythonSourceFiles( SourceFiles(init_injected, sources.unrooted_files), tuple(sorted(source_root_paths)))
async def prepare_python_sources( request: PythonSourceFilesRequest, union_membership: UnionMembership) -> PythonSourceFiles: sources = await Get( SourceFiles, SourceFilesRequest( (tgt.get(SourcesField) for tgt in request.targets), for_sources_types=request.valid_sources_types, enable_codegen=True, ), ) missing_init_files = await Get( AncestorFiles, AncestorFilesRequest(input_files=sources.snapshot.files, requested=("__init__.py", "__init__.pyi")), ) init_injected = await Get( Snapshot, MergeDigests( (sources.snapshot.digest, missing_init_files.snapshot.digest))) # Codegen is able to generate code in any arbitrary location, unlike sources normally being # rooted under the target definition. To determine source roots for these generated files, we # cannot use the normal `SourceRootRequest.for_target()` and we instead must determine # a source root for every individual generated file. So, we re-resolve the codegen sources here. python_and_resources_targets = [] codegen_targets = [] for tgt in request.targets: if tgt.has_field(PythonSourceField) or tgt.has_field( ResourceSourceField): python_and_resources_targets.append(tgt) elif tgt.get(SourcesField).can_generate( PythonSourceField, union_membership) or tgt.get(SourcesField).can_generate( ResourceSourceField, union_membership): codegen_targets.append(tgt) codegen_sources = await MultiGet( Get( HydratedSources, HydrateSourcesRequest( tgt.get(SourcesField), for_sources_types=request.valid_sources_types, enable_codegen=True, ), ) for tgt in codegen_targets) source_root_requests = [ *(SourceRootRequest.for_target(tgt) for tgt in python_and_resources_targets), *(SourceRootRequest.for_file(f) for sources in codegen_sources for f in sources.snapshot.files), ] source_root_objs = await MultiGet( Get(SourceRoot, SourceRootRequest, req) for req in source_root_requests) source_root_paths = { source_root_obj.path for source_root_obj in source_root_objs } return PythonSourceFiles( SourceFiles(init_injected, sources.unrooted_files), tuple(sorted(source_root_paths)))