Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support the option to provide a user-defined output formatter #74

Closed
monkut opened this issue May 23, 2017 · 3 comments
Closed

Support the option to provide a user-defined output formatter #74

monkut opened this issue May 23, 2017 · 3 comments

Comments

@monkut
Copy link

monkut commented May 23, 2017

On trying out fire, one of the first things I wanted was a method to define an output formatter.

At the moment it appears there is a single static formatter defined in _PrintResult().

Consider making an option in Fire:

fire.Fire(OBJECT, formatter=myformatter)

at the moment this is a "formatter" and "outputter". format and output should probably be separated...

@dbieber
Copy link
Member

dbieber commented May 23, 2017

Thanks for raising this issue.

Some brainstorming around the possible ways to approach this:

  1. Pass in a formatter (as you suggested)
  • Do we want a way for the formatter to fall back to the built in serializer?
  1. Allow Fire's serialization to be silenced.
  • Since the result is returned in the output of Fire already, the client can handle the serialization themselves.
  1. Let objects implement something like str or firestr which fire's builtin serialization method will use.
  2. Improve Fire's built in serializer, but don't allow custom serializers.
  • There's definitely value in consistency. E.g. custom serialization probably wouldn't be compatible with piping outputs of one fire command as inputs to another (this feature may just be a pipe dream though :P).

Related note:

  • One potential upcoming change to the serializer is that containers made purely of literals will be handled differently than containers with non-literals.
    As an example, a dict of literals will be serialized in the current way (one key-value pair per line), whereas a dict with functions in it may get shown as a usage string, similar to how an object with properties is shown.

@dbieber
Copy link
Member

dbieber commented Jul 26, 2019

Objects that implement a custom __str__ function will now be serialized with that! (New in v0.2.0.)

@beasteers
Copy link
Contributor

(sorry to comment on an old closed issue, I just didn't want to make issue duplicates)

@dbieber I do love that fire uses the __str__ function of the object, but I still think there's utility in being able to pass a custom formatter as well.

Basically, If I want to format a nested dictionary so that the second level wasn't a single line dict and was, for example indented json or yaml (or something completely different, like filter certain keys, idk), I don't want to have to subclass dict and have that live in the core class/function just so I can provide a custom __str__ formatter.

If you're using basic functions with fire, you can just use a wrapper function to do what you need, but you can't easily do that with object methods especially once you start going down multiple levels of attributes (any recursive class wrapper would have to imitate attributes and __class__, __dict__, __name__, __doc__ and all that that fire leverages and ughhh).

I think just a really simple function that gets called right before the built-in formatter would be amazing because then you can provide custom formatting without having to inject CLI-specific code into the function or class that you're wrapping because that's really what the beauty of fire is imo. Because most of the time that class is being used for something else and then you're like "hey let's use fire and have an ✨automagical✨ CLI !! 💕")

For example

def my_formatter(obj):  # idk if you could include the parsed cli chain too or something
    '''Makes nested dictionaries output as indented json'''
    if isinstance(obj, dict):
        # returns an object that will be handled by the default formatter
        # if you don't want the default formatter to do anything, you'd return a string.
        return {
            k: json.dumps(v, indent=4) if isinstance(v, dict) else v
            for k, v in obj.items()
        }
    return obj

import fire
fire.Fire(formatter=my_formatter)

# somewhere deep inside fire
def some_fire_result_handler(..., formatter=None):
    result = func(*a, **kw)
    ...
    # +++
    if formatter:
        result = formatter(result)
    # +++
    print(builtin_formatter(result)

And another useful thing would be an exception formatter but idk the best way to handle that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants