Beispiel #1
0
def hydrate_struct(address_mapper, address):
    """Given an AddressMapper and an Address, resolve a Struct from a BUILD file.

  Recursively collects any embedded addressables within the Struct, but will not walk into a
  dependencies field, since those should be requested explicitly by rules.
  """

    address_family = yield Get(AddressFamily, Dir(address.spec_path))

    struct = address_family.addressables.get(address)
    addresses = address_family.addressables
    if not struct or address not in addresses:
        _raise_did_you_mean(address_family, address.target_name)
    # TODO: This is effectively: "get the BuildFileAddress for this Address".
    #  see https://github.com/pantsbuild/pants/issues/6657
    address = next(build_address for build_address in addresses
                   if build_address == address)

    inline_dependencies = []

    def maybe_append(outer_key, value):
        if isinstance(value, str):
            if outer_key != 'dependencies':
                inline_dependencies.append(
                    Address.parse(
                        value,
                        relative_to=address.spec_path,
                        subproject_roots=address_mapper.subproject_roots))
        elif isinstance(value, Struct):
            collect_inline_dependencies(value)

    def collect_inline_dependencies(item):
        for key, value in sorted(item._asdict().items(), key=_key_func):
            if not AddressableDescriptor.is_addressable(item, key):
                continue
            if isinstance(value, MutableMapping):
                for _, v in sorted(value.items(), key=_key_func):
                    maybe_append(key, v)
            elif isinstance(value, MutableSequence):
                for v in value:
                    maybe_append(key, v)
            else:
                maybe_append(key, value)

    # Recursively collect inline dependencies from the fields of the struct into `inline_dependencies`.
    collect_inline_dependencies(struct)

    # And then hydrate the inline dependencies.
    hydrated_inline_dependencies = yield [
        Get(HydratedStruct, Address, a) for a in inline_dependencies
    ]
    dependencies = [d.value for d in hydrated_inline_dependencies]

    def maybe_consume(outer_key, value):
        if isinstance(value, str):
            if outer_key == 'dependencies':
                # Don't recurse into the dependencies field of a Struct, since those will be explicitly
                # requested by tasks. But do ensure that their addresses are absolute, since we're
                # about to lose the context in which they were declared.
                value = Address.parse(
                    value,
                    relative_to=address.spec_path,
                    subproject_roots=address_mapper.subproject_roots)
            else:
                value = dependencies[maybe_consume.idx]
                maybe_consume.idx += 1
        elif isinstance(value, Struct):
            value = consume_dependencies(value)
        return value

    # NB: Some pythons throw an UnboundLocalError for `idx` if it is a simple local variable.
    maybe_consume.idx = 0

    # 'zip' the previously-requested dependencies back together as struct fields.
    def consume_dependencies(item, args=None):
        hydrated_args = args or {}
        for key, value in sorted(item._asdict().items(), key=_key_func):
            if not AddressableDescriptor.is_addressable(item, key):
                hydrated_args[key] = value
                continue

            if isinstance(value, MutableMapping):
                container_type = type(value)
                hydrated_args[key] = container_type(
                    (k, maybe_consume(key, v))
                    for k, v in sorted(value.items(), key=_key_func))
            elif isinstance(value, MutableSequence):
                container_type = type(value)
                hydrated_args[key] = container_type(
                    maybe_consume(key, v) for v in value)
            else:
                hydrated_args[key] = maybe_consume(key, value)
        return _hydrate(type(item), address.spec_path, **hydrated_args)

    yield HydratedStruct(
        consume_dependencies(struct, args={'address': address}))
Beispiel #2
0
async def hydrate_struct(address_mapper: AddressMapper,
                         address: Address) -> HydratedStruct:
    """Given an AddressMapper and an Address, resolve a Struct from a BUILD file.

  Recursively collects any embedded addressables within the Struct, but will not walk into a
  dependencies field, since those should be requested explicitly by rules.
  """

    address_family = await Get[AddressFamily](Dir(address.spec_path))

    # NB: `address_family.addressables` is a dictionary of `BuildFileAddress`es and we look it up
    # with an `Address`. This works because `BuildFileAddress` is a subclass, but MyPy warns that it
    # could be a bug.
    struct = address_family.addressables.get(
        address)  # type: ignore[call-overload]
    addresses = address_family.addressables
    if not struct or address not in addresses:
        _raise_did_you_mean(address_family, address.target_name)
    # TODO: This is effectively: "get the BuildFileAddress for this Address".
    #  see https://github.com/pantsbuild/pants/issues/6657
    address = next(build_address for build_address in addresses
                   if build_address == address)

    inline_dependencies = []

    def maybe_append(outer_key, value):
        if isinstance(value, str):
            if outer_key != "dependencies":
                inline_dependencies.append(
                    Address.parse(
                        value,
                        relative_to=address.spec_path,
                        subproject_roots=address_mapper.subproject_roots,
                    ))
        elif isinstance(value, Struct):
            collect_inline_dependencies(value)

    def collect_inline_dependencies(item):
        for key, value in sorted(item._asdict().items(), key=_key_func):
            if not AddressableDescriptor.is_addressable(item, key):
                continue
            if isinstance(value, MutableMapping):
                for _, v in sorted(value.items(), key=_key_func):
                    maybe_append(key, v)
            elif isinstance(value, MutableSequence):
                for v in value:
                    maybe_append(key, v)
            else:
                maybe_append(key, value)

    # Recursively collect inline dependencies from the fields of the struct into `inline_dependencies`.
    collect_inline_dependencies(struct)

    # And then hydrate the inline dependencies.
    hydrated_inline_dependencies = await MultiGet(
        Get[HydratedStruct](Address, a) for a in inline_dependencies)
    dependencies = [d.value for d in hydrated_inline_dependencies]

    def maybe_consume(outer_key, value):
        if isinstance(value, str):
            if outer_key == "dependencies":
                # Don't recurse into the dependencies field of a Struct, since those will be explicitly
                # requested by tasks. But do ensure that their addresses are absolute, since we're
                # about to lose the context in which they were declared.
                value = Address.parse(
                    value,
                    relative_to=address.spec_path,
                    subproject_roots=address_mapper.subproject_roots,
                )
            else:
                value = dependencies[maybe_consume.idx]
                maybe_consume.idx += 1
        elif isinstance(value, Struct):
            value = consume_dependencies(value)
        return value

    # NB: Some pythons throw an UnboundLocalError for `idx` if it is a simple local variable.
    # TODO(#8496): create a decorator for functions which declare a sentinel variable like this!
    maybe_consume.idx = 0  # type: ignore[attr-defined]

    # 'zip' the previously-requested dependencies back together as struct fields.
    def consume_dependencies(item, args=None):
        hydrated_args = args or {}
        for key, value in sorted(item._asdict().items(), key=_key_func):
            if not AddressableDescriptor.is_addressable(item, key):
                hydrated_args[key] = value
                continue

            if isinstance(value, MutableMapping):
                container_type = type(value)
                hydrated_args[key] = container_type(
                    (k, maybe_consume(key, v))
                    for k, v in sorted(value.items(), key=_key_func))
            elif isinstance(value, MutableSequence):
                container_type = type(value)
                hydrated_args[key] = container_type(
                    maybe_consume(key, v) for v in value)
            else:
                hydrated_args[key] = maybe_consume(key, value)
        return _hydrate(type(item), address.spec_path, **hydrated_args)

    return HydratedStruct(
        consume_dependencies(struct, args={"address": address}))
Beispiel #3
0
async def hydrate_struct(address_mapper: AddressMapper,
                         address: Address) -> HydratedStruct:
    """Given an AddressMapper and an Address, resolve a Struct from a BUILD file.

    Recursively collects any embedded addressables within the Struct, but will not walk into a
    dependencies field, since those should be requested explicitly by rules.
    """
    build_file_address = await Get[BuildFileAddress](Address, address)
    address_family = await Get[AddressFamily](Dir(address.spec_path))
    struct = address_family.addressables.get(build_file_address)

    inline_dependencies = []

    def maybe_append(outer_key, value):
        if isinstance(value, str):
            if outer_key != "dependencies":
                inline_dependencies.append(
                    Address.parse(
                        value,
                        relative_to=address.spec_path,
                        subproject_roots=address_mapper.subproject_roots,
                    ))
        elif isinstance(value, Struct):
            collect_inline_dependencies(value)

    def collect_inline_dependencies(item):
        for key, value in sorted(item._asdict().items(), key=_key_func):
            if not AddressableDescriptor.is_addressable(item, key):
                continue
            if isinstance(value, Mapping):
                for _, v in sorted(value.items(), key=_key_func):
                    maybe_append(key, v)
            elif isinstance(value, (list, tuple)):
                for v in value:
                    maybe_append(key, v)
            else:
                maybe_append(key, value)

    # Recursively collect inline dependencies from the fields of the struct into `inline_dependencies`.
    collect_inline_dependencies(struct)

    # And then hydrate the inline dependencies.
    hydrated_inline_dependencies = await MultiGet(
        Get[HydratedStruct](Address, a) for a in inline_dependencies)
    dependencies = tuple(d.value for d in hydrated_inline_dependencies)

    def maybe_consume(outer_key, value):
        if isinstance(value, str):
            if outer_key == "dependencies":
                # Don't recurse into the dependencies field of a Struct, since those will be explicitly
                # requested by tasks. But do ensure that their addresses are absolute, since we're
                # about to lose the context in which they were declared.
                value = Address.parse(
                    value,
                    relative_to=address.spec_path,
                    subproject_roots=address_mapper.subproject_roots,
                )
            else:
                value = dependencies[maybe_consume.idx]
                maybe_consume.idx += 1
        elif isinstance(value, Struct):
            value = consume_dependencies(value)
        return value

    # NB: Some pythons throw an UnboundLocalError for `idx` if it is a simple local variable.
    # TODO(#8496): create a decorator for functions which declare a sentinel variable like this!
    maybe_consume.idx = 0  # type: ignore[attr-defined]

    # 'zip' the previously-requested dependencies back together as struct fields.
    def consume_dependencies(item, args=None):
        hydrated_args = args or {}
        for key, value in sorted(item._asdict().items(), key=_key_func):
            if not AddressableDescriptor.is_addressable(item, key):
                hydrated_args[key] = value
                continue

            if isinstance(value, Mapping):
                hydrated_args[key] = {
                    k: maybe_consume(key, v)
                    for k, v in sorted(value.items(), key=_key_func)
                }
            elif isinstance(value, (list, tuple)):
                hydrated_args[key] = tuple(
                    maybe_consume(key, v) for v in value)
            else:
                hydrated_args[key] = maybe_consume(key, value)
        return _hydrate(type(item), address.spec_path, **hydrated_args)

    return HydratedStruct(
        consume_dependencies(struct, args={"address": address}))