async def find_changed_owners(request: ChangedRequest) -> ChangedAddresses: owners = await Get(Owners, OwnersRequest(request.sources)) if request.dependees == DependeesOption.NONE: return ChangedAddresses(owners) dependees_with_roots = await Get( Dependees, DependeesRequest( owners, transitive=request.dependees == DependeesOption.TRANSITIVE, include_roots=True, ), ) return ChangedAddresses(dependees_with_roots)
async def find_changed_owners(request: ChangedRequest, specs_filter: SpecsFilter) -> ChangedAddresses: no_dependees = request.dependees == DependeesOption.NONE owners = await Get( Owners, OwnersRequest( request.sources, # If `--changed-dependees` is used, we cannot eagerly filter out root targets. We # need to first find their dependees, and only then should we filter. See # https://github.com/pantsbuild/pants/issues/15544 filter_by_global_options=no_dependees, ), ) if no_dependees: return ChangedAddresses(owners) # See https://github.com/pantsbuild/pants/issues/15313. We filter out target generators because # they are not useful as aliases for their generated targets in the context of # `--changed-since`. Including them makes it look like all sibling targets from the same # target generator have also changed. # # However, we also must be careful to preserve if target generators are direct owners, which # happens when a generated file is deleted. owner_target_generators = FrozenOrderedSet( addr.maybe_convert_to_target_generator() for addr in owners if addr.is_generated_target) dependees = await Get( Dependees, DependeesRequest( owners, transitive=request.dependees == DependeesOption.TRANSITIVE, include_roots=False, ), ) result = FrozenOrderedSet(owners) | (dependees - owner_target_generators) if specs_filter.is_specified: # Finally, we must now filter out the result to only include what matches our tags, as the # last step of https://github.com/pantsbuild/pants/issues/15544. # # Note that we use `UnexpandedTargets` rather than `Targets` or `FilteredTargets` so that # we preserve target generators. result_as_tgts = await Get(UnexpandedTargets, Addresses(result)) result = FrozenOrderedSet(tgt.address for tgt in result_as_tgts if specs_filter.matches(tgt)) return ChangedAddresses(result)
async def py_constraints( addresses: Addresses, console: Console, py_constraints_subsystem: PyConstraintsSubsystem, python_setup: PythonSetup, registered_target_types: RegisteredTargetTypes, union_membership: UnionMembership, ) -> PyConstraintsGoal: if py_constraints_subsystem.summary: if addresses: console.print_stderr( "The `py-constraints --summary` goal does not take file/target arguments. Run " "`help py-constraints` for more details." ) return PyConstraintsGoal(exit_code=1) with_generated_targets, without_generated_targets = await MultiGet( Get(Targets, AddressSpecs([DescendantAddresses("")])), Get(UnexpandedTargets, AddressSpecs([DescendantAddresses("")])), ) all_python_targets = sorted( { t for t in (*with_generated_targets, *without_generated_targets) if t.has_field(InterpreterConstraintsField) }, key=lambda tgt: cast(Address, tgt.address), ) constraints_per_tgt = [ InterpreterConstraints.create_from_targets([tgt], python_setup) for tgt in all_python_targets ] transitive_targets_per_tgt = await MultiGet( Get(TransitiveTargets, TransitiveTargetsRequest([tgt.address])) for tgt in all_python_targets ) transitive_constraints_per_tgt = [ InterpreterConstraints.create_from_targets(transitive_targets.closure, python_setup) for transitive_targets in transitive_targets_per_tgt ] dependees_per_root = await MultiGet( Get(Dependees, DependeesRequest([tgt.address], transitive=True, include_roots=False)) for tgt in all_python_targets ) data = [ { "Target": tgt.address.spec, "Constraints": str(constraints), "Transitive Constraints": str(transitive_constraints), "# Dependencies": len(transitive_targets.dependencies), "# Dependees": len(dependees), } for tgt, constraints, transitive_constraints, transitive_targets, dependees in zip( all_python_targets, constraints_per_tgt, transitive_constraints_per_tgt, transitive_targets_per_tgt, dependees_per_root, ) ] with py_constraints_subsystem.output_sink(console) as stdout: writer = csv.DictWriter( stdout, fieldnames=[ "Target", "Constraints", "Transitive Constraints", "# Dependencies", "# Dependees", ], ) writer.writeheader() for entry in data: writer.writerow(entry) return PyConstraintsGoal(exit_code=0) transitive_targets = await Get(TransitiveTargets, TransitiveTargetsRequest(addresses)) final_constraints = InterpreterConstraints.create_from_targets( transitive_targets.closure, python_setup ) if not final_constraints: target_types_with_constraints = sorted( tgt_type.alias for tgt_type in registered_target_types.types if tgt_type.class_has_field(InterpreterConstraintsField, union_membership) ) logger.warning( "No Python files/targets matched for the `py-constraints` goal. All target types with " f"Python interpreter constraints: {', '.join(target_types_with_constraints)}" ) return PyConstraintsGoal(exit_code=0) constraints_to_addresses = defaultdict(set) for tgt in transitive_targets.closure: constraints = InterpreterConstraints.create_from_targets([tgt], python_setup) if not constraints: continue constraints_to_addresses[constraints].add(tgt.address) with py_constraints_subsystem.output(console) as output_stdout: output_stdout(f"Final merged constraints: {final_constraints}\n") if len(addresses) > 1: merged_constraints_warning = ( "(These are the constraints used if you were to depend on all of the input " "files/targets together, even though they may end up never being used together in " "the real world. Consider using a more precise query or running " "`./pants py-constraints --summary`.)\n" ) output_stdout(indent(fill(merged_constraints_warning, 80), " ")) for constraint, addrs in sorted(constraints_to_addresses.items()): output_stdout(f"\n{constraint}\n") for addr in sorted(addrs): output_stdout(f" {addr}\n") return PyConstraintsGoal(exit_code=0)