Consider commands as pm-list, pm-update or user-list (not in drush still). Those commands print out information in a table format and are also allowed to print out raw data suitable for pipelining. The --pipe output.
Columns and the form of pipe output is hardcoded. Several issues have raised asking to add some columns or discussing on how the pipe output should looks like. The proposal here is to implement an output engine for tabular data flexible enough to let the user customize their outputs.
Here's a description of the proposal:
1/ Engines can be classes since PMTNG 1, the next natural step is to let them receive parameters to be configured. We also want to configure them from the command definitions:
$items['user-list'] = array(
'engines' => array('output_tabular'),
'output_tabular' => array(
'columns' => array('uid', 'name', 'mail' , 'status', 'roles'), // All of the available keys in data objects
'column names' => array('User ID', 'User name', 'E-mail', 'User status', 'User roles',),
'table columns' => array('uid', 'name', 'mail', 'status'), // Columns for table output
'pipe columns' => array('name', 'mail'), // Allow for a composed output
'pipe separator' => '-', // Separator for the composed output: name1-mail1, name2-mail2
'pipe delimiter' => ',', // For a CSV output
)
);
Obviously pipe separator or pipe delimiter can be default values not needed to be defined in each command. Also, the pipe output could default to the first of the table columns.
2/ Commands implementing output_tabular engine will be cleaner, with no need to compose the table rows or the pipe output. They could just return output_tabular_print($data);.
$data may be a keyed array of arrays or objects that must define all of the columns from the command definition.
3/ With the above in place, the output engine options could be: table, pipe, json, yaml,... json or yaml may output all of the columns.
4/ Output is custimizable with command line options such as --table-columns="uid,name"
This, although laborious, doesn't seem hard to implement and may be suitable for drush 5.
| Comment | File | Size | Author |
|---|---|---|---|
| #9 | drush-1364808.patch | 17.71 KB | jonhattan |
Comments
Comment #1
moshe weitzman commentedSounds pretty good to me ... An alternative that occurs to me that is shipping with some example bash/awk code in example.bashrc that demonstrates parsing our CSV output. We could also add examples for common tricks like xargs. This wouldn't get you json/yaml etc. but would make dealing with our output easier.
Comment #2
jonhattanComment #3
msonnabaum commentedGlad to find this issue already started. I've run into a few commands lately that don't have --pipe (test-run) and it would be great to make this more consistent. I also really like the idea of supporting any of the --format options.
Is this something we need to tie to the concept of tables though? Perhaps it would be too abstract otherwise. Not sure yet.
Comment #4
moshe weitzman commentedIf this gets done real soon, it can happily go into Drush5. But I don't think we will hold release for it. Removing tag.
Comment #5
jonhattanAfter a chat with Mark I think of this:
Consider two output engines: output_tabular and output_object. Both using
--format=for the command line.output_tabular is almost expressed in OP. Their engine types are table, csv, pipe...
output_object's engine types are: var_dump, json, yaml (#1366098: Automatically download Symfony YAML component). It could print one or several objects. It can be extended in contrib with new formats (#1396178: Add properties output as a --format)...
The big difference between both engines is that output_tabular must declare its objects' columns. For the rest, output_tabular is a subclass of output_object, so tabular commands can just print json et al.
Commands optionally declare its output type. Perhaps with
'output' => tabular | objectinstead of including the engine explicitly. That is, engine options become first level command options.If a command declares its output type, the command callback is not responsible of printing out. It just needs to return $data and drush will send it to the engine.
Comment #6
clemens.tolboomFor http://drupal.org/project/drush_entity we have --fields which are a more general concept then columns. With fields you can ask for a path in the array/object tree.
Apart from the column names which are hard do define on ie entity_info() results it would be nice for this issue to support path as drush_entity does.
could be defined as
drush entity-type-read user node --fields=label,bundles/*/labelthus this defined the data path and Column name at once. In this example 'Bundle label' value occurs multiple for each node type (and user).
For more --fields examples see http://build2be.com/content/drush-entity-support-patch-needs-review
Comment #7
damiankloip commentedI think me and Clemens started a similar issue (http://drupal.org/node/1441026) in the drush queue, but this is probably a more substantial and comprehensive solution to the whole formats issue. I am happy to do some work on this, I really want to see more unified --format, --pipe options etc...
How do you see contrib modules adding/extending the available formats?
Comment #8
jonhattanWith this proposal contrib could extend formats by defining an engine.
Comment #9
jonhattanI wonder if the title is more comprehensible now.
Attached is a format-patch with three commits:
1. Extend engines subsystem to allow for parametrized engines.
2. Implements the output engine. I called it printout. A better name is welcome.
3. Changed pm-releases command to use printout engine.
I started from the perspective of a table to get something running. This patch still needs work and focus to define a broad coverage of use cases: better support other formats and be usable by other commands. Just set it to needs review to get some feedback.
Secondary anottation: I added a new file
includes/engines.incwith some new declarations. The idea is to put in it all code related to engines from commands.inc and drush.inc. however it should be deferred to the end of this issue or to a follow-up for a better understanding.Explanation of commits in the patch:
1. Parametrized engines: This allows to shape a engine for a particular use case. For example a tabular engine must be parametrized with name of valid fields.
1a) Parameters are defined in the engine_type declaration (i.e. hook_drush_engine_type_info()) as:
1b) Parametrization is done in the command declaration. For example in pm-releases:
1c) Parametrized engines must implement drush_parametrized_engine interface.
1d) Parameters are passed to the engine constructor.
1e) Those engines need to alter command help in order to fill in parametrized values in the help text.
2. Implementation of printout engine.
Implemented table, pipe, json and yaml for the sake of completeness. json and yaml are just quick&dirty for demonstration. Not much more to say here.
3. Changes in pm-releases.
The command doesn't deal with table business. Basicly it obtains the releases and pass them to the engine. So its implementation is simplified.
I'm not satisfied with:
Perhaps it can be managed by introducing parametrization at engine level (parametrization explained above is at engine_type level, that is, for all implementations of a engine_type).
4. Screenshots
a) Help text is parametrized. See the fields option.
If you'd like to see the options grouped by engine, goto #1421570: Command options grouping
b) Standard usageworks as before:
... as before.
c) Pipe output by default prints the first of the default fields (version).
d) Pipe output with three fields and custom separator and delimiter.
e) Json output defaults to all fields:
f) Json with limited fields:
Comment #10
moshe weitzman commentedTHis looks terrific to me. I'd love input from drush users and drush maintainers.
Comment #11
moshe weitzman commentedI should say that I looked at the code and found only minor issues:
Comment #12
damiankloip commentedjonhattan, this patch looks pretty awesome. I think it's a much more solid and flexible solution going forward. We can probably ditch #1441026: Add a hook_drush_output_formats in that case too :)
Comment #13
moshe weitzman commented@jonhattan - I'm ready to create a branch for 5.x and then commit this to master. Lets just address my comments in #11 .
Comment #14
greg.1.anderson commentedSorry for being late to the party. The code looks really good, and I like the direction. The one thing that occurs to me is that some code is going to want to output a 2d array, and other code is going to want to output a 1d array. Some data types, like xml and json, are well-suited to both types, but it is kind of hard to shoehorn a 1d array into a table, or a 2d array into --pipe output. Maybe it would be helpful if commands specified the kind of output they produced, and output engines specified what kind of data they could format. This would be helpful for formatting command help output (list relevant available output formats in command help). This augmentation is minor and could be a follow-on issue, though.
pm-releases is an example of a command that produces 2d data that works well as either json or tabular output, but what about something like pm-updatecode? That command produces tabular output, but when representing this data as structured output (xml, yaml or json), it would be most useful to output an associative array, where one column of the tabular output (e.g. the module machine name) is the key, and the other columns in the table are returned as a sub-array value of that key. Another example where this would be useful is
drush statusoutput as json or xml, which ideally would be represented as name:value pairs. It looks like the existing design could be extended to allow this option. Without this feature, the json output of drush status would be something like [["Drush version", "6.0-dev"],["PHP configuration", "/etc/php5/cli/php.ini"]] (or if done literally from the existing code, [["Drush version", ":", "6.0-dev"],["PHP configuration", ":", "/etc/php5/cli/php.ini"]]), which would not be as useful as an associative-array-based output. Actually, the output drush status might instead be simply ["6.0-dev","/etc/php5/cli/php.ini"], but that is also somewhat inconvenient, since the fields output by drush status is variable depending on the status of the site being examined.I might be inclined to say that we should figure out how
drush statusshould be implemented with this scheme before committing, but I suppose that I wouldn't mind if this was committed to a drush-6 branch w/out the associative arrays feature. I suspect we are going to need it before this feature is generally useful, though.Comment #15
moshe weitzman commentedLets get
drush statusworking nicely before committingComment #16
moshe weitzman commentedfbcmd, a CLI for facebook, has a similar feature. See http://fbcmd.dtompkins.com/output.
Hope someone can get back to this. It is close,
Comment #17
greg.1.anderson commentedThis should be unified with #1866344: Make --pipe awesome with output format engines for more awesome. That issue pretty well takes care of #14. 'pipe' is a mode, not a format, though; see #1866344, which allows --pipe=json, etc. (as different commands may have different output formats for --pipe). One thing I realized, though, is that some formats are only good in regular (non-pipe) mode, and others are best in pipe mode. We could further unify --pipe and --format by making the later put Drush into pipe mode for certain formats, and make --pipe shorthand for --format=[pipe default format].
Comment #18
greg.1.anderson commentedContinuing work at #1866344: Make --pipe awesome with output format engines.
Comment #19
greg.1.anderson commentedClosed #1866344 as a duplicate of #1366098: Automatically download Symfony YAML component.