On Thu, Mar 17, 2022, 6:53 AM Hanna Reitz <hre...@redhat.com> wrote:

> On 09.03.22 04:53, John Snow wrote:
> > qemu_img_json() is a new helper built on top of qemu_img() that tries to
> > pull a valid JSON document out of the stdout stream.
> >
> > In the event that the return code is negative (the program crashed), or
> > the code is greater than zero and did not produce valid JSON output, the
> > VerboseProcessError raised by qemu_img() is re-raised.
> >
> > In the event that the return code is zero but we can't parse valid JSON,
> > allow the JSON deserialization error to be raised.
> >
> > Signed-off-by: John Snow <js...@redhat.com>
> > ---
> >   tests/qemu-iotests/iotests.py | 38 +++++++++++++++++++++++++++++++++++
> >   1 file changed, 38 insertions(+)
> >
> > diff --git a/tests/qemu-iotests/iotests.py
> b/tests/qemu-iotests/iotests.py
> > index 7057db0686..546b142a6c 100644
> > --- a/tests/qemu-iotests/iotests.py
> > +++ b/tests/qemu-iotests/iotests.py
> > @@ -276,6 +276,44 @@ def ordered_qmp(qmsg, conv_keys=True):
> >   def qemu_img_create(*args: str) -> subprocess.CompletedProcess[str]:
> >       return qemu_img('create', *args)
> >
> > +def qemu_img_json(*args: str) -> Any:
> > +    """
> > +    Run qemu-img and return its output as deserialized JSON.
> > +
> > +    :raise CalledProcessError:
> > +        When qemu-img crashes, or returns a non-zero exit code without
> > +        producing a valid JSON document to stdout.
> > +    :raise JSONDecoderError:
> > +        When qemu-img returns 0, but failed to produce a valid JSON
> document.
> > +
> > +    :return: A deserialized JSON object; probably a dict[str, Any].
> > +    """
> > +    json_data = ...  # json.loads can legitimately return 'None'.
>
> What kind of arcane sigil is this?
>

I may genuinely start referring to it as the Arcane Sigil.


> I’m trying to read into it, but it seems like...  Well, I won’t swear on
> a public list.  As far as I understand, it’s just a special singleton
> object that you can use for whatever you think is an appropriate use for
> an ellipsis?  And so in this case you use it as a special singleton
> object that would never legitimately appear, to be separate from None?
>
> You’re really, really good at making me hate Python a bit more with
> every series.
>

I aim to please.

Yes, it's just Another Singleton That Isn't None, because technically a
JSON document can be just "null". Will qemu_img() ever, ever print that
single string all by itself?

Well, I hope not, but. I felt guilty not addressing that technicality
somehow.


> It also just doesn’t seem very useful to me in this case.  I’m not sure
> whether this notation is widely known in the Python world, but I have
> only myself to go off of, and I was just very confused, so I would
> prefer not to have this in the code.
>

You're right, it's a bit too clever. I judged the cleverness:usefulness
ratio poorly.

(I think it's a trick that a lot of long time python people know, because
sooner or later one wants to distinguish between an explicitly provided
"None" and a default value that signifies "No argument provided". It's
definitely a hack. It's not something most users would know.)


> > +
> > +    try:
> > +        res = qemu_img(*args, combine_stdio=False)
> > +    except subprocess.CalledProcessError as exc:
> > +        # Terminated due to signal. Don't bother.
> > +        if exc.returncode < 0:
> > +            raise
> > +
> > +        # Commands like 'check' can return failure (exit codes 2 and 3)
> > +        # to indicate command completion, but with errors found. For
> > +        # multi-command flexibility, ignore the exact error codes and
> > +        # *try* to load JSON.
> > +        try:
> > +            json_data = json.loads(exc.stdout)
>
> Why not `return json.loads(exc.stdout)`?
>

Uh.


> > +        except json.JSONDecodeError:
> > +            # Nope. This thing is toast. Raise the process error.
> > +            pass
> > +
> > +        if json_data is ...:
> > +            raise
>
> And just unconditionally `raise` here.


Uhhh.


> > +
> > +    if json_data is ...:
> > +        json_data = json.loads(res.stdout)
> > +    return json_data
>
> And just `return json.loads(res.stdout)` here, without any condition.
>

Uhhhhhhhh...!

Yeah, that's obviously way better. I'm sorry to have subjected you to an
arcane workaround for no reason :/


>
> Hanna
>
> > +
> >   def qemu_img_measure(*args):
> >       return json.loads(qemu_img_pipe("measure", "--output", "json",
> *args))
> >
>
>

Reply via email to