glotzerlab/signac

Document how to use $in and pass a list in the CLI

rodrigolece opened this issue · 5 comments

For the life of me, I don't know how to pass a list inside a CLI filter.

Description

A concise description of what is causing an issue and what you expected to happen.

To reproduce

I've tried

python project.py run -f model \$in [a, b]

the more explicit JSON

python project.py run -f {"model": {"\$in": ["a", "b"]}}

and even the incorrect JSON (I imagine that the pairs convention could be causing trouble)

python project.py run -f {"model": {"\$in": "[a, b]"}}

In every case I get the error

Interpreted filter arguments as '{"model": "$in", "[a,": "b"}'

telling me that signac doesn't recognise the comma and the brackets, but why wouldn't it parse JSON?

System configuration

  • Version of Python: 3.8
  • Version of signac: 1.7.0

Also, I found it very confusing that CLI is basically only discussed in the Tutorial (which is fantastic, very clear) and a paragraph in the query API, but this issue isn't clear at all and almost all the documentation discusses the filters from inside Python!

Still work to do from the closed issue #95 ?

bdice commented

Thanks for the issue! I think the short answer to your problem is to wrap the JSON input in single quotes. The shell will treat content in single quotes literally and not insert variables / etc.

Here's a longer demo that should help demonstrate this. (Using the current master branch of signac, based on 1.7.0, and the current master of signac-flow, based on 0.17.0.)

Initialize a project with job statepoints {"a": 0}, {"a": 1}, {"a": 2}. Search for the last two:

$ signac init test_project
Initialized project 'test_project'.
$ signac shell -c "for i in range(3): pr.open_job({'a': i}).init()"
$ signac find '{"a": {"$in": [1, 2]}}'
9f8a8e5ba8c70c774d410a9107e2a32b
42b7b4f2921788ea14dac5566e6f06d0

Then create a FlowProject:

$ flow init
Created file 'project.py'.

Edit the file:

# project.py
from flow import FlowProject


class Project(FlowProject):
    pass

@Project.operation
def print_job(job):
    print(job, job.sp)

if __name__ == '__main__':
    Project().main()

Back to the CLI:

$ python project.py run -f '{"a": {"$in": [1, 2]}}'
Using environment configuration: StandardEnvironment
9f8a8e5ba8c70c774d410a9107e2a32b {'a': 2}
42b7b4f2921788ea14dac5566e6f06d0 {'a': 1}
WARNING:flow.project:Operation 'print_job' has no postconditions!

Thank you @bdice, the wrapped JSON in single quotes worked perfectly!

Do you have any plans to document the CLI further ?

bdice commented

@rodrigolece We added a CLI reference here somewhat recently. https://docs.signac.io/projects/core/en/latest/cli.html

Does this CLI reference help? It sounds like you're seeking more tutorial-like information, not just a command reference. I agree that the CLI hasn't gotten as much attention as the Python API, especially in tutorials.

If we expand tutorials to include stuff like using single quotes to escape JSON, we'll need to make it clear which pieces of syntax are bash-specific. I don't think that other shells (or Windows, for that matter) would act exactly the same, but I'd need to test it to know for sure.

vyasr commented

The only other thing I'd add to @bdice's explanation here is that in your original query if you wanted to use the abbreviated syntax without typing out the full query (which is what your first example was trying to do), the key again is escaping and quoting the correct parts:

python project.py run -f model \$in '["a", "b"]'

Getting the in operator to cooperate on the CLI is tricky because the syntax for lists needs to be valid at multiple points: your shell's parser, our shortcut->full JSON conversion, and the final JSON itself. @bdice is correct that the syntax for both quoting and escaping (mainly the $ sign) is bash-specific, so I'm not sure how much that we could say. It is probably safe to assume that the majority of signac users are using Bourne shell derivatives, so we could potentially give some specific instructions in that case but make very clear that this would not be portable to other shells.