qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Max Reitz <mreitz@redhat.com>
To: John Snow <jsnow@redhat.com>, qemu-block@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>, qemu-devel@nongnu.org
Subject: Re: [PATCH 04/67] iotests.py: create_test_image, remove_test_image
Date: Wed, 2 Oct 2019 13:00:03 +0200	[thread overview]
Message-ID: <8604aec2-2a09-7bcc-792a-9e11e8b6fb11@redhat.com> (raw)
In-Reply-To: <a65b0919-66ee-d1e6-2ba1-94e7ec8fb184@redhat.com>


[-- Attachment #1.1: Type: text/plain, Size: 6123 bytes --]

On 02.10.19 01:20, John Snow wrote:
> 
> 
> On 10/1/19 3:46 PM, Max Reitz wrote:
>> Python tests should use these two new functions instead of
>> qemu_img('create', ...) + os.remove(), so that user-supplied image
>> options are interpreted and handled correctly.
>>
>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>> ---
>>  tests/qemu-iotests/iotests.py | 56 +++++++++++++++++++++++++++++++++++
>>  1 file changed, 56 insertions(+)
>>
>> diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
>> index b5ea424de4..fce1ab04c9 100644
>> --- a/tests/qemu-iotests/iotests.py
>> +++ b/tests/qemu-iotests/iotests.py
>> @@ -122,6 +122,62 @@ def qemu_img_create(*args):
>>  
>>      return qemu_img(*args)
>>  
>> +def create_test_image(filename, size=None, fmt=imgfmt, opts=[],
>> +                      backing_file=None, backing_fmt=None,
>> +                      objects=[], unsafe=False):
> 
> Python! It's the language that everybody loves and can do no wrong!
> 
> Ah, wait, no, maybe the opposite.
> 
> You want this:
> 
> (..., opts=None, ...):
>     opts = opts or []
> 
> because, unfortunately, default parameters are bound at definition time
> and not at call time, so the default list here is like a static local.

OK.  Interesting.

I suppose the same goes for @objects, then.

>> +    if fmt == imgfmt:
>> +        # Only use imgopts for the default format
>> +        opts = imgopts + opts
>> +
>> +    for i, opt in enumerate(opts):
>> +        if '$TEST_IMG' in opt:
>> +            opts[i] = opt.replace('$TEST_IMG', filename)
>> +
>> +    # default luks support
>> +    if fmt == 'luks':
>> +        if not any('key-secret' in opt for opt in opts):
> 
> You can write "if not 'key-secret' in opts"

Oh, that’s recursive?

>> +            opts.append(luks_default_key_secret_opt)
> 
> And here we might modify that default list.
> 
>> +        objects.append(luks_default_secret_object)
>> +
>> +    args = ['create', '-f', fmt]
>> +
>> +    if len(opts) > 0:
>> +        args += ['-o', ','.join(opts)]
>> +
>> +    if backing_file is not None:
>> +        args += ['-b', backing_file]
>> +
>> +    if backing_fmt is not None:
>> +        args += ['-F', backing_fmt]
>> +
>> +    if len(objects) > 0:
>> +        # Generate a [['--object', $obj], [...], ...] list and flatten it
>> +        args += [arg for objarg in (['--object', obj] for obj in objects) \
>> +                     for arg in objarg]
> 
> I may have mentioned at one point that I love comprehensions, but
> dislike nested comprehensions.

I can’t remember but I do remember writing this piece of code, being sad
that there is no .flatten, and wanting everyone to see the monster that
arises.

> At this point, I think it's far simpler
> to say:
> 
> for obj in objects:
>     args.extend(['--object', obj])
> 
> or, even shorter:
>     args += ['--object', obj]

OK, so now you saw it, I’m glad to make the flattening more flattering
to read.

>> +
>> +    if unsafe:
>> +        args.append('-u')
>> +
>> +    args.append(filename)
>> +    if size is not None:
>> +        args.append(str(size))
>> +
>> +    return qemu_img(*args)
>> +
>> +# Use this to remove images create with create_test_image in the
> 
> created
> 
> and you might as well move the # comment to a """docstring""" while
> you're here.
> 
>> +# default image format (iotests.imgfmt)
>> +def remove_test_image(filename):
>> +    try:
>> +        os.remove(filename)
>> +
>> +        data_file = next(opt.replace('data_file=', '') \
>> +                            .replace('$TEST_IMG', filename) \
>> +                         for opt in imgopts if opt.startswith('data_file='))
>> +
> 
> Learned something today: you can use next() to get the first value from
> a generator expression.

I was sad for a bit that Python doesn’t have a find(), but then I
noticed this works as well.  (Already used extensively in “iotests: Add
VM.assert_block_path()” from my “block: Fix check_to_replace_node()”
series.)

>> +        os.remove(data_file)
> 
> Keep in mind that if the generator expression returns no results, that
> next() will throw an exception and we won't make it here. That's ok, but,

I did.  If there are no results, it’s good we won’t get here.

This code would be wrong if the next() didn’t throw an exception.

>> +    except:
>> +        pass
>> +
> 
> The unqualified except doesn't help me know which errors you expected
> and which you didn't.

What I’m expecting: FileNotFound, StopIteration.

But the thing is that I feel like maybe removing a file should always
pass, regardless of the exact exception.  (I can imagine to be wrong.)

> We have a function like this elsewhere in the python directory:
> 
> def remove_if_exists(filename):
>     try:
>         os.remove(filename)
>     except FileNotFoundError:
>         pass

We do?  I can’t find it.  I find a _remove_if_exists in machine.py,
which I’m not sure whether it’s supposed to be used outside, and it
works a bit different, actually (but probably to the same effect).

> Can we use that here and remove the try:/except: from this function? It
> will require you to change the list search to something like this instead:
> 
> remove_if_exists(filename)
> for opt in (x for x in imgopts if etc):
>     data_file = opt.replace('etc', 'etc')
>     remove_if_exists(data_file)
> 
> to avoid the exception when you call next().

I don’t know why I’d avoid the exception, though.

This is probably because I don’t like pythonic code, again, but I prefer
a next() + exception over a for loop that just iterates once or not at all.

>>  def qemu_img_verbose(*args):
>>      '''Run qemu-img without suppressing its output and return the exit code'''
>>      exitcode = subprocess.call(qemu_img_args + list(args))
>>
> 
> My fussiness with the remove() function is just optional picky stuff,
> but the rest matters, I think.

OK.  Indeed it does!

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

  parent reply	other threads:[~2019-10-02 11:02 UTC|newest]

Thread overview: 81+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-10-01 19:46 [PATCH 00/67] iotests: Honor $IMGOPTS in Python tests Max Reitz
2019-10-01 19:46 ` [PATCH 01/67] iotests.py: Read $IMGOPTS Max Reitz
2019-10-01 22:16   ` John Snow
2019-10-03 15:03     ` Vladimir Sementsov-Ogievskiy
2019-10-03 15:08   ` Vladimir Sementsov-Ogievskiy
2019-10-01 19:46 ` [PATCH 02/67] iotests.py: Add @skip_for_imgopts() Max Reitz
2019-10-01 22:16   ` John Snow
2019-10-03 15:19   ` Vladimir Sementsov-Ogievskiy
2019-10-04 12:55     ` Max Reitz
2019-10-01 19:46 ` [PATCH 03/67] iotests.py: Add unsupported_imgopts Max Reitz
2019-10-01 22:18   ` John Snow
2019-10-03 15:21   ` Vladimir Sementsov-Ogievskiy
2019-10-01 19:46 ` [PATCH 04/67] iotests.py: create_test_image, remove_test_image Max Reitz
2019-10-01 23:20   ` John Snow
2019-10-01 23:30     ` John Snow
2019-10-02 11:00     ` Max Reitz [this message]
2019-10-03  0:35       ` John Snow
2019-10-01 19:46 ` [PATCH 05/67] iotests.py: Add ImagePaths Max Reitz
2019-10-01 19:46 ` [PATCH 06/67] iotests.py: Add image_path() Max Reitz
2019-10-01 19:46 ` [PATCH 07/67] iotests.py: Filter data_file in filter_img_info Max Reitz
2019-10-01 19:46 ` [PATCH 08/67] iotests.py: Add filter_json_filename() Max Reitz
2019-10-01 19:46 ` [PATCH 09/67] iotests.py: Add @hide_fields to img_info_log Max Reitz
2019-10-01 19:46 ` [PATCH 10/67] iotests/169: Skip persistent cases for compat=0.10 Max Reitz
2019-10-01 19:46 ` [PATCH 11/67] iotests/224: Filter json:{} from commit command Max Reitz
2019-10-01 19:46 ` [PATCH 12/67] iotests/228: Filter json:{} filenames Max Reitz
2019-10-01 19:46 ` [PATCH 13/67] iotests/242: Hide refcount bit information Max Reitz
2019-10-01 19:46 ` [PATCH 14/67] iotests: Use unsupported_imgopts in Python tests Max Reitz
2019-10-01 19:46 ` [PATCH 15/67] iotests/030: Honor $IMGOPTS Max Reitz
2019-10-01 19:46 ` [PATCH 16/67] iotests/040: " Max Reitz
2019-10-01 19:46 ` [PATCH 17/67] iotests/041: " Max Reitz
2019-10-01 19:46 ` [PATCH 18/67] iotests/044: " Max Reitz
2019-10-01 19:46 ` [PATCH 19/67] iotests/045: " Max Reitz
2019-10-01 19:46 ` [PATCH 20/67] iotests/055: " Max Reitz
2019-10-01 19:46 ` [PATCH 21/67] iotests/056: " Max Reitz
2019-10-01 19:46 ` [PATCH 22/67] iotests/057: " Max Reitz
2019-10-01 19:46 ` [PATCH 23/67] iotests/065: " Max Reitz
2019-10-01 19:46 ` [PATCH 24/67] iotests/096: " Max Reitz
2019-10-01 19:46 ` [PATCH 25/67] iotests/118: " Max Reitz
2019-10-01 19:46 ` [PATCH 26/67] iotests/124: " Max Reitz
2019-10-01 19:46 ` [PATCH 27/67] iotests/129: " Max Reitz
2019-10-01 19:46 ` [PATCH 28/67] iotests/132: " Max Reitz
2019-10-01 19:46 ` [PATCH 29/67] iotests/139: " Max Reitz
2019-10-01 19:46 ` [PATCH 30/67] iotests/147: " Max Reitz
2019-10-01 19:46 ` [PATCH 31/67] iotests/148: " Max Reitz
2019-10-01 19:46 ` [PATCH 32/67] iotests/151: " Max Reitz
2019-10-01 19:46 ` [PATCH 33/67] iotests/152: " Max Reitz
2019-10-01 19:46 ` [PATCH 34/67] iotests/155: " Max Reitz
2019-10-01 19:46 ` [PATCH 35/67] iotests/163: " Max Reitz
2019-10-01 19:46 ` [PATCH 36/67] iotests/165: " Max Reitz
2019-10-01 19:46 ` [PATCH 37/67] iotests/169: " Max Reitz
2019-10-01 19:46 ` [PATCH 38/67] iotests/194: " Max Reitz
2019-10-01 19:46 ` [PATCH 39/67] iotests/196: " Max Reitz
2019-10-01 19:46 ` [PATCH 40/67] iotests/199: " Max Reitz
2019-10-01 19:46 ` [PATCH 41/67] iotests/202: " Max Reitz
2019-10-01 19:46 ` [PATCH 42/67] iotests/203: " Max Reitz
2019-10-01 19:46 ` [PATCH 43/67] iotests/205: " Max Reitz
2019-10-01 19:46 ` [PATCH 44/67] iotests/208: " Max Reitz
2019-10-01 19:46 ` [PATCH 45/67] " Max Reitz
2019-10-01 19:46 ` [PATCH 46/67] iotests/216: " Max Reitz
2019-10-01 19:46 ` [PATCH 47/67] iotests/218: " Max Reitz
2019-10-01 19:46 ` [PATCH 48/67] iotests/219: " Max Reitz
2019-10-01 19:46 ` [PATCH 49/67] iotests/222: " Max Reitz
2019-10-01 19:46 ` [PATCH 50/67] iotests/224: " Max Reitz
2019-10-01 19:46 ` [PATCH 51/67] iotests/228: " Max Reitz
2019-10-01 19:47 ` [PATCH 52/67] iotests/234: " Max Reitz
2019-10-01 19:47 ` [PATCH 53/67] iotests/235: " Max Reitz
2019-10-01 19:47 ` [PATCH 54/67] iotests/236: " Max Reitz
2019-10-01 19:47 ` [PATCH 55/67] iotests/237: " Max Reitz
2019-10-01 19:47 ` [PATCH 56/67] iotests/242: " Max Reitz
2019-10-01 19:47 ` [PATCH 57/67] iotests/245: " Max Reitz
2019-10-01 19:47 ` [PATCH 58/67] iotests/246: " Max Reitz
2019-10-01 19:47 ` [PATCH 59/67] iotests/248: " Max Reitz
2019-10-01 19:47 ` [PATCH 60/67] iotests/254: " Max Reitz
2019-10-01 19:47 ` [PATCH 61/67] iotests/255: " Max Reitz
2019-10-01 19:47 ` [PATCH 62/67] iotests/256: " Max Reitz
2019-10-01 19:47 ` [PATCH 63/67] iotests/257: " Max Reitz
2019-10-01 19:47 ` [PATCH 64/67] iotests/258: " Max Reitz
2019-10-01 19:47 ` [PATCH 65/67] iotests/262: " Max Reitz
2019-10-01 19:47 ` [PATCH 66/67] iotests.py: Forbid qemu_img*('create', ...) Max Reitz
2019-10-01 19:47 ` [PATCH 67/67] iotests.py: Drop qemu_img_log(), qemu_img_create() Max Reitz
2019-10-01 21:47 ` [PATCH 00/67] iotests: Honor $IMGOPTS in Python tests John Snow

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8604aec2-2a09-7bcc-792a-9e11e8b6fb11@redhat.com \
    --to=mreitz@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).