コード例 #1
0
def replace_components(root, component, base_class):
    """
        Recursively look at all fields in config to find where `component` would fit.
        This is used to change configs so that they don't use default values.
        Return the chain of field names, from child to parent.
    """
    for k, v in get_config_fields(root).items():
        default, _, options = v
        if options and component in options:
            return [k]
        else:
            v_comp_name = get_component_name(default)
            v_comp_obj = next(iter(find_config_class(v_comp_name)), None)
            if v_comp_obj:
                found = replace_components(v_comp_obj, component, base_class)
                if found:
                    found.append(k)
                    return found

                # Not found in options, try to match base classes
                # Except ConfigBase, which gives false matches
                bases = list(
                    filter(lambda x: x != ConfigBase, v_comp_obj.__bases__))
                if base_class & set(bases):
                    return [k]
コード例 #2
0
def get_config_fields(obj):
    """
    Return a dict of config help for this object, where:
    - key: config name
    - value: (default, type, options)

        - default: default value for this key if not specified
        - type: type for this config value, as a string
        - options: possible values for this config, only if type = Union

    If the type is "Union", the options give the lists of class names that
    are possible, and the default is one of those class names.
    """
    ret = {}
    members = get_class_members_recursive(obj)
    # Add fields with type by no default value
    typed_members = [k for k in members.get("__annotations__", {})]
    for k in typed_members:
        if k not in members:
            members[k] = None
    if issubclass(obj.__class__, Enum):
        opt = members.get("_member_names_")
        if opt is not None:
            typing = obj.__name__
            ret[typing] = (opt[0], typing, set(opt))
            return ret
        else:
            return obj

    for k, v in sorted(members.items()):
        if k.startswith("_"):
            continue
        typing = members.get("__annotations__", {}).get(k)
        if issubclass(type(v), Enum):
            ret[k] = (v._name_, typing, set(type(v)._member_names_))
        elif not typing:
            if not isfunction(v):
                ret[k] = (v, None, None)
        # type(Union) changed from Py3.6 to 3.7
        elif hasattr(typing, "__origin__") and typing.__origin__ == Union:
            options = set()
            for t in typing.__args__:
                options.add(get_component_name(t))
            ret[k] = (v, None, options)
        else:
            ret[k] = (v, get_component_name(typing), None)
    return ret
コード例 #3
0
def pretty_print_config_class(obj):
    """Pretty-print the fields of one object."""
    parent_class_name = ""
    if hasattr(obj, "__bases__"):
        parent_classes = (b.__name__ for b in obj.__bases__)
        parent_class_name = ", ".join(parent_classes)
        print(f"=== {obj.__module__}.{obj.__name__} ({parent_class_name}) ===")
    else:
        print(f"=== {obj.__module__}.{obj.__name__} ===")
    if obj.__doc__:
        print(f'"""{obj.__doc__.strip()}"""')

    config_help = get_config_fields(obj)
    if issubclass(obj, Enum):
        for k, v in config_help.items():
            default, typing, options = v
            print(f"    {k}: ({typing})")
            for o in options:
                print(f"         {o}")
        return

    for k, v in config_help.items():
        default, typing, options = v
        if hasattr(default, "__module__"):
            default_value = get_component_name(default)
        else:
            default_value = default

        if typing and options:  # Enum
            print(f"    {k}: ({typing.__name__})")
            for o in options:
                if o == default_value:
                    print(f"         {o} (default)")
                else:
                    print(f"         {o}")
        elif options:  # Union
            print(f"    {k}: (one of)")
            for o in options:
                if o == default_value:
                    print(f"         {o} (default)")
                else:
                    print(f"         {o}")
        elif default and typing:
            print(f"    {k}: {typing} = {default_value}")
        elif default:
            print(f"    {k} = {default_value}")
        elif typing:
            print(f"    {k}: {typing}")
        else:
            print(f"    {k} = null")