diff mbox series

[v3,6/8] iotests: Test driver whitelisting in 093

Message ID 20190819201851.24418-7-mreitz@redhat.com
State New
Headers show
Series iotests: Selfish patches | expand

Commit Message

Max Reitz Aug. 19, 2019, 8:18 p.m. UTC
null-aio may not be whitelisted.  Skip all test cases that require it.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 tests/qemu-iotests/093 | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

Comments

Thomas Huth Aug. 20, 2019, 6:40 a.m. UTC | #1
On 8/19/19 10:18 PM, Max Reitz wrote:
> null-aio may not be whitelisted.  Skip all test cases that require it.
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  tests/qemu-iotests/093 | 12 +++++++++---
>  1 file changed, 9 insertions(+), 3 deletions(-)
> 
> diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
> index 50c1e7f2ec..f03fa24a07 100755
> --- a/tests/qemu-iotests/093
> +++ b/tests/qemu-iotests/093
> @@ -24,7 +24,7 @@ import iotests
>  nsec_per_sec = 1000000000
>  
>  class ThrottleTestCase(iotests.QMPTestCase):
> -    test_img = "null-aio://"
> +    test_driver = "null-aio"
>      max_drives = 3
>  
>      def blockstats(self, device):
> @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
>                  return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
>          raise Exception("Device not found for blockstats: %s" % device)
>  
> +    def required_drivers(self):
> +        return [self.test_driver]
> +
> +    @iotests.skip_if_unsupported(required_drivers)
>      def setUp(self):
>          self.vm = iotests.VM()
>          for i in range(0, self.max_drives):
> -            self.vm.add_drive(self.test_img, "file.read-zeroes=on")
> +            self.vm.add_drive(self.test_driver + "://", "file.read-zeroes=on")
>          self.vm.launch()
>  
>      def tearDown(self):
> @@ -264,7 +268,7 @@ class ThrottleTestCase(iotests.QMPTestCase):
>          self.assertEqual(self.blockstats('drive1')[0], 4096)
>  
>  class ThrottleTestCoroutine(ThrottleTestCase):
> -    test_img = "null-co://"
> +    test_driver = "null-co"
>  
>  class ThrottleTestGroupNames(iotests.QMPTestCase):
>      max_drives = 3
> @@ -425,4 +429,6 @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase):
>  
>  
>  if __name__ == '__main__':
> +    if 'null-co' not in iotests.supported_formats():
> +        iotests.notrun('null-co driver support missing')
>      iotests.main(supported_fmts=["raw"])

Maybe also mention null-co in the patch description?

 Thomas
Max Reitz Aug. 20, 2019, 12:23 p.m. UTC | #2
On 20.08.19 08:40, Thomas Huth wrote:
> On 8/19/19 10:18 PM, Max Reitz wrote:
>> null-aio may not be whitelisted.  Skip all test cases that require it.
>>
>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>> ---
>>  tests/qemu-iotests/093 | 12 +++++++++---
>>  1 file changed, 9 insertions(+), 3 deletions(-)
>>
>> diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
>> index 50c1e7f2ec..f03fa24a07 100755
>> --- a/tests/qemu-iotests/093
>> +++ b/tests/qemu-iotests/093
>> @@ -24,7 +24,7 @@ import iotests
>>  nsec_per_sec = 1000000000
>>  
>>  class ThrottleTestCase(iotests.QMPTestCase):
>> -    test_img = "null-aio://"
>> +    test_driver = "null-aio"
>>      max_drives = 3
>>  
>>      def blockstats(self, device):
>> @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
>>                  return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
>>          raise Exception("Device not found for blockstats: %s" % device)
>>  
>> +    def required_drivers(self):
>> +        return [self.test_driver]
>> +
>> +    @iotests.skip_if_unsupported(required_drivers)
>>      def setUp(self):
>>          self.vm = iotests.VM()
>>          for i in range(0, self.max_drives):
>> -            self.vm.add_drive(self.test_img, "file.read-zeroes=on")
>> +            self.vm.add_drive(self.test_driver + "://", "file.read-zeroes=on")
>>          self.vm.launch()
>>  
>>      def tearDown(self):
>> @@ -264,7 +268,7 @@ class ThrottleTestCase(iotests.QMPTestCase):
>>          self.assertEqual(self.blockstats('drive1')[0], 4096)
>>  
>>  class ThrottleTestCoroutine(ThrottleTestCase):
>> -    test_img = "null-co://"
>> +    test_driver = "null-co"
>>  
>>  class ThrottleTestGroupNames(iotests.QMPTestCase):
>>      max_drives = 3
>> @@ -425,4 +429,6 @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase):
>>  
>>  
>>  if __name__ == '__main__':
>> +    if 'null-co' not in iotests.supported_formats():
>> +        iotests.notrun('null-co driver support missing')
>>      iotests.main(supported_fmts=["raw"])
> 
> Maybe also mention null-co in the patch description?

I probably didn’t because I felt bad that maybe I should add a null-co
check to all tests that require it...  But two wrongs don’t make a
right, so I’ll leave it at one wrong and put “Skip the whole test if
null-co is not whitelisted.” into the commit message, yes.

Max
John Snow Aug. 20, 2019, 9:32 p.m. UTC | #3
On 8/19/19 4:18 PM, Max Reitz wrote:
> null-aio may not be whitelisted.  Skip all test cases that require it.
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  tests/qemu-iotests/093 | 12 +++++++++---
>  1 file changed, 9 insertions(+), 3 deletions(-)
> 
> diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
> index 50c1e7f2ec..f03fa24a07 100755
> --- a/tests/qemu-iotests/093
> +++ b/tests/qemu-iotests/093
> @@ -24,7 +24,7 @@ import iotests
>  nsec_per_sec = 1000000000
>  
>  class ThrottleTestCase(iotests.QMPTestCase):
> -    test_img = "null-aio://"
> +    test_driver = "null-aio"
>      max_drives = 3
>  
>      def blockstats(self, device):
> @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
>                  return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
>          raise Exception("Device not found for blockstats: %s" % device)
>  
> +    def required_drivers(self):
> +        return [self.test_driver]
> +
> +    @iotests.skip_if_unsupported(required_drivers)

Oh, I see why you're passing args[0] back to the callback now. Why not
just pass self.required_drivers and call it with no arguments instead?

You can get a bound version that way that doesn't need additional
arguments, and then the callback is free to take generic callables of
any kind.

>      def setUp(self):
>          self.vm = iotests.VM()
>          for i in range(0, self.max_drives):
> -            self.vm.add_drive(self.test_img, "file.read-zeroes=on")
> +            self.vm.add_drive(self.test_driver + "://", "file.read-zeroes=on")
>          self.vm.launch()
>  
>      def tearDown(self):
> @@ -264,7 +268,7 @@ class ThrottleTestCase(iotests.QMPTestCase):
>          self.assertEqual(self.blockstats('drive1')[0], 4096)
>  
>  class ThrottleTestCoroutine(ThrottleTestCase):
> -    test_img = "null-co://"
> +    test_driver = "null-co"
>  
>  class ThrottleTestGroupNames(iotests.QMPTestCase):
>      max_drives = 3
> @@ -425,4 +429,6 @@ class ThrottleTestRemovableMedia(iotests.QMPTestCase):
>  
>  
>  if __name__ == '__main__':
> +    if 'null-co' not in iotests.supported_formats():
> +        iotests.notrun('null-co driver support missing')
>      iotests.main(supported_fmts=["raw"])
>
Max Reitz Aug. 21, 2019, 10:55 a.m. UTC | #4
On 20.08.19 23:32, John Snow wrote:
> 
> 
> On 8/19/19 4:18 PM, Max Reitz wrote:
>> null-aio may not be whitelisted.  Skip all test cases that require it.
>>
>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>> ---
>>  tests/qemu-iotests/093 | 12 +++++++++---
>>  1 file changed, 9 insertions(+), 3 deletions(-)
>>
>> diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
>> index 50c1e7f2ec..f03fa24a07 100755
>> --- a/tests/qemu-iotests/093
>> +++ b/tests/qemu-iotests/093
>> @@ -24,7 +24,7 @@ import iotests
>>  nsec_per_sec = 1000000000
>>  
>>  class ThrottleTestCase(iotests.QMPTestCase):
>> -    test_img = "null-aio://"
>> +    test_driver = "null-aio"
>>      max_drives = 3
>>  
>>      def blockstats(self, device):
>> @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
>>                  return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
>>          raise Exception("Device not found for blockstats: %s" % device)
>>  
>> +    def required_drivers(self):
>> +        return [self.test_driver]
>> +
>> +    @iotests.skip_if_unsupported(required_drivers)
> 
> Oh, I see why you're passing args[0] back to the callback now. Why not
> just pass self.required_drivers and call it with no arguments instead?
> 
> You can get a bound version that way that doesn't need additional
> arguments, and then the callback is free to take generic callables of
> any kind.

That would be nicer indeed.

Max
Max Reitz Sept. 13, 2019, 12:47 p.m. UTC | #5
On 20.08.19 23:32, John Snow wrote:
> 
> 
> On 8/19/19 4:18 PM, Max Reitz wrote:
>> null-aio may not be whitelisted.  Skip all test cases that require it.
>>
>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>> ---
>>  tests/qemu-iotests/093 | 12 +++++++++---
>>  1 file changed, 9 insertions(+), 3 deletions(-)
>>
>> diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
>> index 50c1e7f2ec..f03fa24a07 100755
>> --- a/tests/qemu-iotests/093
>> +++ b/tests/qemu-iotests/093
>> @@ -24,7 +24,7 @@ import iotests
>>  nsec_per_sec = 1000000000
>>  
>>  class ThrottleTestCase(iotests.QMPTestCase):
>> -    test_img = "null-aio://"
>> +    test_driver = "null-aio"
>>      max_drives = 3
>>  
>>      def blockstats(self, device):
>> @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
>>                  return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
>>          raise Exception("Device not found for blockstats: %s" % device)
>>  
>> +    def required_drivers(self):
>> +        return [self.test_driver]
>> +
>> +    @iotests.skip_if_unsupported(required_drivers)
> 
> Oh, I see why you're passing args[0] back to the callback now. Why not
> just pass self.required_drivers and call it with no arguments instead?
> 
> You can get a bound version that way that doesn't need additional
> arguments, and then the callback is free to take generic callables of
> any kind.

Am I doing something wrong?

I just get

+Traceback (most recent call last):
+  File "093", line 26, in <module>
+    class ThrottleTestCase(iotests.QMPTestCase):
+  File "093", line 41, in ThrottleTestCase
+    @iotests.skip_if_unsupported(self.required_drivers)
+NameError: name 'self' is not defined

this way.

Max
John Snow Sept. 13, 2019, 6:30 p.m. UTC | #6
On 9/13/19 8:47 AM, Max Reitz wrote:
> On 20.08.19 23:32, John Snow wrote:
>>
>>
>> On 8/19/19 4:18 PM, Max Reitz wrote:
>>> null-aio may not be whitelisted.  Skip all test cases that require it.
>>>
>>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>>> ---
>>>  tests/qemu-iotests/093 | 12 +++++++++---
>>>  1 file changed, 9 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
>>> index 50c1e7f2ec..f03fa24a07 100755
>>> --- a/tests/qemu-iotests/093
>>> +++ b/tests/qemu-iotests/093
>>> @@ -24,7 +24,7 @@ import iotests
>>>  nsec_per_sec = 1000000000
>>>  
>>>  class ThrottleTestCase(iotests.QMPTestCase):
>>> -    test_img = "null-aio://"
>>> +    test_driver = "null-aio"
>>>      max_drives = 3
>>>  
>>>      def blockstats(self, device):
>>> @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
>>>                  return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
>>>          raise Exception("Device not found for blockstats: %s" % device)
>>>  
>>> +    def required_drivers(self):
>>> +        return [self.test_driver]
>>> +
>>> +    @iotests.skip_if_unsupported(required_drivers)
>>
>> Oh, I see why you're passing args[0] back to the callback now. Why not
>> just pass self.required_drivers and call it with no arguments instead?
>>
>> You can get a bound version that way that doesn't need additional
>> arguments, and then the callback is free to take generic callables of
>> any kind.
> 
> Am I doing something wrong?
> 
> I just get
> 
> +Traceback (most recent call last):
> +  File "093", line 26, in <module>
> +    class ThrottleTestCase(iotests.QMPTestCase):
> +  File "093", line 41, in ThrottleTestCase
> +    @iotests.skip_if_unsupported(self.required_drivers)
> +NameError: name 'self' is not defined
> 
> this way.
> 
> Max
> 
What was I even talking about? :\ Well.

I'd still like to define func_wrapper with a nod to the type constraint
it has:

def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
    [...]


Then, you'd write:

if callable(required_formats):
    fmts = required_formats(instance)
else:
    fmts = required_formats


And:

> +    def required_drivers(self):
> +        return [self.test_driver]
> +
> +    @iotests.skip_if_unsupported(required_drivers)
>      def setUp(self):

The problem is that 'self' isn't defined at the class level, so I was
mistaken about being able to use it :( Python does not have a notion of
a lexical constant to refer to the class being defined; and of course we
do not have an instance variable at definition time.

Sorry for the wild goose chase.

It's fine as-is.

(I wanted a way to define the required_formats callback without forcing
it to be a class method, but the decorator code runs at definition time
and we just don't HAVE the instance; so the way you wrote it is I think
the only way it CAN be written without some much nastier trickery.)
Max Reitz Sept. 17, 2019, 8:18 a.m. UTC | #7
On 13.09.19 20:30, John Snow wrote:
> 
> 
> On 9/13/19 8:47 AM, Max Reitz wrote:
>> On 20.08.19 23:32, John Snow wrote:
>>>
>>>
>>> On 8/19/19 4:18 PM, Max Reitz wrote:
>>>> null-aio may not be whitelisted.  Skip all test cases that require it.
>>>>
>>>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>>>> ---
>>>>  tests/qemu-iotests/093 | 12 +++++++++---
>>>>  1 file changed, 9 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
>>>> index 50c1e7f2ec..f03fa24a07 100755
>>>> --- a/tests/qemu-iotests/093
>>>> +++ b/tests/qemu-iotests/093
>>>> @@ -24,7 +24,7 @@ import iotests
>>>>  nsec_per_sec = 1000000000
>>>>  
>>>>  class ThrottleTestCase(iotests.QMPTestCase):
>>>> -    test_img = "null-aio://"
>>>> +    test_driver = "null-aio"
>>>>      max_drives = 3
>>>>  
>>>>      def blockstats(self, device):
>>>> @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
>>>>                  return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
>>>>          raise Exception("Device not found for blockstats: %s" % device)
>>>>  
>>>> +    def required_drivers(self):
>>>> +        return [self.test_driver]
>>>> +
>>>> +    @iotests.skip_if_unsupported(required_drivers)
>>>
>>> Oh, I see why you're passing args[0] back to the callback now. Why not
>>> just pass self.required_drivers and call it with no arguments instead?
>>>
>>> You can get a bound version that way that doesn't need additional
>>> arguments, and then the callback is free to take generic callables of
>>> any kind.
>>
>> Am I doing something wrong?
>>
>> I just get
>>
>> +Traceback (most recent call last):
>> +  File "093", line 26, in <module>
>> +    class ThrottleTestCase(iotests.QMPTestCase):
>> +  File "093", line 41, in ThrottleTestCase
>> +    @iotests.skip_if_unsupported(self.required_drivers)
>> +NameError: name 'self' is not defined
>>
>> this way.
>>
>> Max
>>
> What was I even talking about? :\ Well.
> 
> I'd still like to define func_wrapper with a nod to the type constraint
> it has:
> 
> def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
>     [...]
> 
> 
> Then, you'd write:
> 
> if callable(required_formats):
>     fmts = required_formats(instance)
> else:
>     fmts = required_formats

Yep, that anyway.  (Although I didn’t know about the “param: type”
syntax and put that constraint in a comment instead.  Thanks again :-))

Max
Max Reitz Sept. 17, 2019, 8:29 a.m. UTC | #8
On 17.09.19 10:18, Max Reitz wrote:
> On 13.09.19 20:30, John Snow wrote:
>>
>>
>> On 9/13/19 8:47 AM, Max Reitz wrote:
>>> On 20.08.19 23:32, John Snow wrote:
>>>>
>>>>
>>>> On 8/19/19 4:18 PM, Max Reitz wrote:
>>>>> null-aio may not be whitelisted.  Skip all test cases that require it.
>>>>>
>>>>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>>>>> ---
>>>>>  tests/qemu-iotests/093 | 12 +++++++++---
>>>>>  1 file changed, 9 insertions(+), 3 deletions(-)
>>>>>
>>>>> diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
>>>>> index 50c1e7f2ec..f03fa24a07 100755
>>>>> --- a/tests/qemu-iotests/093
>>>>> +++ b/tests/qemu-iotests/093
>>>>> @@ -24,7 +24,7 @@ import iotests
>>>>>  nsec_per_sec = 1000000000
>>>>>  
>>>>>  class ThrottleTestCase(iotests.QMPTestCase):
>>>>> -    test_img = "null-aio://"
>>>>> +    test_driver = "null-aio"
>>>>>      max_drives = 3
>>>>>  
>>>>>      def blockstats(self, device):
>>>>> @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
>>>>>                  return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
>>>>>          raise Exception("Device not found for blockstats: %s" % device)
>>>>>  
>>>>> +    def required_drivers(self):
>>>>> +        return [self.test_driver]
>>>>> +
>>>>> +    @iotests.skip_if_unsupported(required_drivers)
>>>>
>>>> Oh, I see why you're passing args[0] back to the callback now. Why not
>>>> just pass self.required_drivers and call it with no arguments instead?
>>>>
>>>> You can get a bound version that way that doesn't need additional
>>>> arguments, and then the callback is free to take generic callables of
>>>> any kind.
>>>
>>> Am I doing something wrong?
>>>
>>> I just get
>>>
>>> +Traceback (most recent call last):
>>> +  File "093", line 26, in <module>
>>> +    class ThrottleTestCase(iotests.QMPTestCase):
>>> +  File "093", line 41, in ThrottleTestCase
>>> +    @iotests.skip_if_unsupported(self.required_drivers)
>>> +NameError: name 'self' is not defined
>>>
>>> this way.
>>>
>>> Max
>>>
>> What was I even talking about? :\ Well.
>>
>> I'd still like to define func_wrapper with a nod to the type constraint
>> it has:
>>
>> def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
>>     [...]
>>
>>
>> Then, you'd write:
>>
>> if callable(required_formats):
>>     fmts = required_formats(instance)
>> else:
>>     fmts = required_formats
> 
> Yep, that anyway.  (Although I didn’t know about the “param: type”
> syntax and put that constraint in a comment instead.  Thanks again :-))

Ah, but it isn’t a constraint, but just a “type hint” and the
interpreter doesn’t enforce it.  How quaint.

Well, better than a comment anyway.

Max
Max Reitz Sept. 17, 2019, 8:32 a.m. UTC | #9
On 17.09.19 10:29, Max Reitz wrote:
> On 17.09.19 10:18, Max Reitz wrote:
>> On 13.09.19 20:30, John Snow wrote:
>>>
>>>
>>> On 9/13/19 8:47 AM, Max Reitz wrote:
>>>> On 20.08.19 23:32, John Snow wrote:
>>>>>
>>>>>
>>>>> On 8/19/19 4:18 PM, Max Reitz wrote:
>>>>>> null-aio may not be whitelisted.  Skip all test cases that require it.
>>>>>>
>>>>>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>>>>>> ---
>>>>>>  tests/qemu-iotests/093 | 12 +++++++++---
>>>>>>  1 file changed, 9 insertions(+), 3 deletions(-)
>>>>>>
>>>>>> diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
>>>>>> index 50c1e7f2ec..f03fa24a07 100755
>>>>>> --- a/tests/qemu-iotests/093
>>>>>> +++ b/tests/qemu-iotests/093
>>>>>> @@ -24,7 +24,7 @@ import iotests
>>>>>>  nsec_per_sec = 1000000000
>>>>>>  
>>>>>>  class ThrottleTestCase(iotests.QMPTestCase):
>>>>>> -    test_img = "null-aio://"
>>>>>> +    test_driver = "null-aio"
>>>>>>      max_drives = 3
>>>>>>  
>>>>>>      def blockstats(self, device):
>>>>>> @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
>>>>>>                  return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
>>>>>>          raise Exception("Device not found for blockstats: %s" % device)
>>>>>>  
>>>>>> +    def required_drivers(self):
>>>>>> +        return [self.test_driver]
>>>>>> +
>>>>>> +    @iotests.skip_if_unsupported(required_drivers)
>>>>>
>>>>> Oh, I see why you're passing args[0] back to the callback now. Why not
>>>>> just pass self.required_drivers and call it with no arguments instead?
>>>>>
>>>>> You can get a bound version that way that doesn't need additional
>>>>> arguments, and then the callback is free to take generic callables of
>>>>> any kind.
>>>>
>>>> Am I doing something wrong?
>>>>
>>>> I just get
>>>>
>>>> +Traceback (most recent call last):
>>>> +  File "093", line 26, in <module>
>>>> +    class ThrottleTestCase(iotests.QMPTestCase):
>>>> +  File "093", line 41, in ThrottleTestCase
>>>> +    @iotests.skip_if_unsupported(self.required_drivers)
>>>> +NameError: name 'self' is not defined
>>>>
>>>> this way.
>>>>
>>>> Max
>>>>
>>> What was I even talking about? :\ Well.
>>>
>>> I'd still like to define func_wrapper with a nod to the type constraint
>>> it has:
>>>
>>> def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
>>>     [...]
>>>
>>>
>>> Then, you'd write:
>>>
>>> if callable(required_formats):
>>>     fmts = required_formats(instance)
>>> else:
>>>     fmts = required_formats
>>
>> Yep, that anyway.  (Although I didn’t know about the “param: type”
>> syntax and put that constraint in a comment instead.  Thanks again :-))
> 
> Ah, but it isn’t a constraint, but just a “type hint” and the
> interpreter doesn’t enforce it.  How quaint.

Oh, and they were introduced only in 3.5.  Maybe that’s a tad too new.

> Well, better than a comment anyway.

So maybe not.

Max
Kevin Wolf Sept. 17, 2019, 8:40 a.m. UTC | #10
Am 17.09.2019 um 10:18 hat Max Reitz geschrieben:
> On 13.09.19 20:30, John Snow wrote:
> > 
> > 
> > On 9/13/19 8:47 AM, Max Reitz wrote:
> >> On 20.08.19 23:32, John Snow wrote:
> >>>
> >>>
> >>> On 8/19/19 4:18 PM, Max Reitz wrote:
> >>>> null-aio may not be whitelisted.  Skip all test cases that require it.
> >>>>
> >>>> Signed-off-by: Max Reitz <mreitz@redhat.com>
> >>>> ---
> >>>>  tests/qemu-iotests/093 | 12 +++++++++---
> >>>>  1 file changed, 9 insertions(+), 3 deletions(-)
> >>>>
> >>>> diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
> >>>> index 50c1e7f2ec..f03fa24a07 100755
> >>>> --- a/tests/qemu-iotests/093
> >>>> +++ b/tests/qemu-iotests/093
> >>>> @@ -24,7 +24,7 @@ import iotests
> >>>>  nsec_per_sec = 1000000000
> >>>>  
> >>>>  class ThrottleTestCase(iotests.QMPTestCase):
> >>>> -    test_img = "null-aio://"
> >>>> +    test_driver = "null-aio"
> >>>>      max_drives = 3
> >>>>  
> >>>>      def blockstats(self, device):
> >>>> @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
> >>>>                  return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
> >>>>          raise Exception("Device not found for blockstats: %s" % device)
> >>>>  
> >>>> +    def required_drivers(self):
> >>>> +        return [self.test_driver]
> >>>> +
> >>>> +    @iotests.skip_if_unsupported(required_drivers)
> >>>
> >>> Oh, I see why you're passing args[0] back to the callback now. Why not
> >>> just pass self.required_drivers and call it with no arguments instead?
> >>>
> >>> You can get a bound version that way that doesn't need additional
> >>> arguments, and then the callback is free to take generic callables of
> >>> any kind.
> >>
> >> Am I doing something wrong?
> >>
> >> I just get
> >>
> >> +Traceback (most recent call last):
> >> +  File "093", line 26, in <module>
> >> +    class ThrottleTestCase(iotests.QMPTestCase):
> >> +  File "093", line 41, in ThrottleTestCase
> >> +    @iotests.skip_if_unsupported(self.required_drivers)
> >> +NameError: name 'self' is not defined
> >>
> >> this way.
> >>
> >> Max
> >>
> > What was I even talking about? :\ Well.
> > 
> > I'd still like to define func_wrapper with a nod to the type constraint
> > it has:
> > 
> > def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
> >     [...]
> > 
> > 
> > Then, you'd write:
> > 
> > if callable(required_formats):
> >     fmts = required_formats(instance)
> > else:
> >     fmts = required_formats
> 
> Yep, that anyway.  (Although I didn’t know about the “param: type”
> syntax and put that constraint in a comment instead.  Thanks again :-))

Note that function annotations are Python 3 only, so we can't use that
syntax yet anyway. If you want to use type hints that are understood by
tools (like mypy) and compatible with Python 2, you have to use
something like this (feel free to be more specific than Any):

    def func_wrapper(instance, *args, **kwargs):
        # type: (iotests.QMPTestCase, Any, Any) -> None
        [...]

Or if you only want to declare the type for one parameter:

    def func_wrapper(instance,  # type: iotests.QMPTestCase
                     *args,
                     **kwargs):
        [...]

Especially the latter syntax might be sufficiently ugly that just doing
a free-form comment that can't be parsed by tools is justifiable.

Kevin
Max Reitz Sept. 17, 2019, 11:07 a.m. UTC | #11
On 17.09.19 10:40, Kevin Wolf wrote:
> Am 17.09.2019 um 10:18 hat Max Reitz geschrieben:
>> On 13.09.19 20:30, John Snow wrote:
>>>
>>>
>>> On 9/13/19 8:47 AM, Max Reitz wrote:
>>>> On 20.08.19 23:32, John Snow wrote:
>>>>>
>>>>>
>>>>> On 8/19/19 4:18 PM, Max Reitz wrote:
>>>>>> null-aio may not be whitelisted.  Skip all test cases that require it.
>>>>>>
>>>>>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>>>>>> ---
>>>>>>  tests/qemu-iotests/093 | 12 +++++++++---
>>>>>>  1 file changed, 9 insertions(+), 3 deletions(-)
>>>>>>
>>>>>> diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
>>>>>> index 50c1e7f2ec..f03fa24a07 100755
>>>>>> --- a/tests/qemu-iotests/093
>>>>>> +++ b/tests/qemu-iotests/093
>>>>>> @@ -24,7 +24,7 @@ import iotests
>>>>>>  nsec_per_sec = 1000000000
>>>>>>  
>>>>>>  class ThrottleTestCase(iotests.QMPTestCase):
>>>>>> -    test_img = "null-aio://"
>>>>>> +    test_driver = "null-aio"
>>>>>>      max_drives = 3
>>>>>>  
>>>>>>      def blockstats(self, device):
>>>>>> @@ -35,10 +35,14 @@ class ThrottleTestCase(iotests.QMPTestCase):
>>>>>>                  return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
>>>>>>          raise Exception("Device not found for blockstats: %s" % device)
>>>>>>  
>>>>>> +    def required_drivers(self):
>>>>>> +        return [self.test_driver]
>>>>>> +
>>>>>> +    @iotests.skip_if_unsupported(required_drivers)
>>>>>
>>>>> Oh, I see why you're passing args[0] back to the callback now. Why not
>>>>> just pass self.required_drivers and call it with no arguments instead?
>>>>>
>>>>> You can get a bound version that way that doesn't need additional
>>>>> arguments, and then the callback is free to take generic callables of
>>>>> any kind.
>>>>
>>>> Am I doing something wrong?
>>>>
>>>> I just get
>>>>
>>>> +Traceback (most recent call last):
>>>> +  File "093", line 26, in <module>
>>>> +    class ThrottleTestCase(iotests.QMPTestCase):
>>>> +  File "093", line 41, in ThrottleTestCase
>>>> +    @iotests.skip_if_unsupported(self.required_drivers)
>>>> +NameError: name 'self' is not defined
>>>>
>>>> this way.
>>>>
>>>> Max
>>>>
>>> What was I even talking about? :\ Well.
>>>
>>> I'd still like to define func_wrapper with a nod to the type constraint
>>> it has:
>>>
>>> def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
>>>     [...]
>>>
>>>
>>> Then, you'd write:
>>>
>>> if callable(required_formats):
>>>     fmts = required_formats(instance)
>>> else:
>>>     fmts = required_formats
>>
>> Yep, that anyway.  (Although I didn’t know about the “param: type”
>> syntax and put that constraint in a comment instead.  Thanks again :-))
> 
> Note that function annotations are Python 3 only, so we can't use that
> syntax yet anyway. If you want to use type hints that are understood by
> tools (like mypy) and compatible with Python 2, you have to use
> something like this (feel free to be more specific than Any):

Do we really feel like staying compatible with Python 2, though?

Max
Kevin Wolf Sept. 17, 2019, 11:22 a.m. UTC | #12
Am 17.09.2019 um 13:07 hat Max Reitz geschrieben:
> On 17.09.19 10:40, Kevin Wolf wrote:
> > Am 17.09.2019 um 10:18 hat Max Reitz geschrieben:
> >> On 13.09.19 20:30, John Snow wrote:
> >>> I'd still like to define func_wrapper with a nod to the type constraint
> >>> it has:
> >>>
> >>> def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
> >>>     [...]
> >>>
> >>>
> >>> Then, you'd write:
> >>>
> >>> if callable(required_formats):
> >>>     fmts = required_formats(instance)
> >>> else:
> >>>     fmts = required_formats
> >>
> >> Yep, that anyway.  (Although I didn’t know about the “param: type”
> >> syntax and put that constraint in a comment instead.  Thanks again :-))
> > 
> > Note that function annotations are Python 3 only, so we can't use that
> > syntax yet anyway. If you want to use type hints that are understood by
> > tools (like mypy) and compatible with Python 2, you have to use
> > something like this (feel free to be more specific than Any):
> 
> Do we really feel like staying compatible with Python 2, though?

Feel like it? No.

It's more that we are compelled to do so because we only deprecated it
in 4.1.

Kevin
John Snow Sept. 17, 2019, 1:09 p.m. UTC | #13
On 9/17/19 7:22 AM, Kevin Wolf wrote:
> Am 17.09.2019 um 13:07 hat Max Reitz geschrieben:
>> On 17.09.19 10:40, Kevin Wolf wrote:
>>> Am 17.09.2019 um 10:18 hat Max Reitz geschrieben:
>>>> On 13.09.19 20:30, John Snow wrote:
>>>>> I'd still like to define func_wrapper with a nod to the type constraint
>>>>> it has:
>>>>>
>>>>> def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
>>>>>     [...]
>>>>>
>>>>>
>>>>> Then, you'd write:
>>>>>
>>>>> if callable(required_formats):
>>>>>     fmts = required_formats(instance)
>>>>> else:
>>>>>     fmts = required_formats
>>>>
>>>> Yep, that anyway.  (Although I didn’t know about the “param: type”
>>>> syntax and put that constraint in a comment instead.  Thanks again :-))
>>>
>>> Note that function annotations are Python 3 only, so we can't use that
>>> syntax yet anyway. If you want to use type hints that are understood by
>>> tools (like mypy) and compatible with Python 2, you have to use
>>> something like this (feel free to be more specific than Any):
>>
>> Do we really feel like staying compatible with Python 2, though?
> 
> Feel like it? No.
> 
> It's more that we are compelled to do so because we only deprecated it
> in 4.1.
> 
> Kevin
> 

Sorry for the impromptu lesson on type hints in 3.5! I added that in to
my suggestion as a demonstrative example and didn't mean for you to use
it as-is, sorry for not making that clear.

I'm confused about the Python3 deprecation timeline. Normally we'd
follow our standard approach, but it does hit EOL at the end of this
year, so do we drop support then, too? I have the memory of a goldfish I
suppose, and can't quite remember our conclusions, if any, of previous
discussions on this subject.

If we do drop python2 though, the new minimum version appears to be 3.5
because that's what ships in EPEL. That'd give us standardized type
hints that we can use for static analysis tools.
Kevin Wolf Sept. 17, 2019, 1:42 p.m. UTC | #14
Am 17.09.2019 um 15:09 hat John Snow geschrieben:
> On 9/17/19 7:22 AM, Kevin Wolf wrote:
> > Am 17.09.2019 um 13:07 hat Max Reitz geschrieben:
> >> On 17.09.19 10:40, Kevin Wolf wrote:
> >>> Am 17.09.2019 um 10:18 hat Max Reitz geschrieben:
> >>>> On 13.09.19 20:30, John Snow wrote:
> >>>>> I'd still like to define func_wrapper with a nod to the type constraint
> >>>>> it has:
> >>>>>
> >>>>> def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
> >>>>>     [...]
> >>>>>
> >>>>>
> >>>>> Then, you'd write:
> >>>>>
> >>>>> if callable(required_formats):
> >>>>>     fmts = required_formats(instance)
> >>>>> else:
> >>>>>     fmts = required_formats
> >>>>
> >>>> Yep, that anyway.  (Although I didn’t know about the “param: type”
> >>>> syntax and put that constraint in a comment instead.  Thanks again :-))
> >>>
> >>> Note that function annotations are Python 3 only, so we can't use that
> >>> syntax yet anyway. If you want to use type hints that are understood by
> >>> tools (like mypy) and compatible with Python 2, you have to use
> >>> something like this (feel free to be more specific than Any):
> >>
> >> Do we really feel like staying compatible with Python 2, though?
> > 
> > Feel like it? No.
> > 
> > It's more that we are compelled to do so because we only deprecated it
> > in 4.1.
> 
> Sorry for the impromptu lesson on type hints in 3.5! I added that in to
> my suggestion as a demonstrative example and didn't mean for you to use
> it as-is, sorry for not making that clear.
> 
> I'm confused about the Python3 deprecation timeline. Normally we'd
> follow our standard approach, but it does hit EOL at the end of this
> year, so do we drop support then, too? I have the memory of a goldfish I
> suppose, and can't quite remember our conclusions, if any, of previous
> discussions on this subject.

It shouldn't make a difference actually because deprecation in 4.1 means
that 4.2 (in December) will be the last release that must still support
Python 2, and we can switch to Python 3 for 5.0.

> If we do drop python2 though, the new minimum version appears to be 3.5
> because that's what ships in EPEL. That'd give us standardized type
> hints that we can use for static analysis tools.

Actually I seem to remember I suggested that we should make 3.5 the
minimum Python 3 version, and I thought a patch to this effect had been
merged, but now I can't find any such check in configure. Maybe I should
find the old thread again to see if there was any reason not to do this.

Personally, I would have preferred 3.6 because it brings in variable
annotations, but I think last time the conclusion was that it would be
3.5 indeed.

Kevin
John Snow Sept. 17, 2019, 1:44 p.m. UTC | #15
On 9/17/19 9:42 AM, Kevin Wolf wrote:
> Am 17.09.2019 um 15:09 hat John Snow geschrieben:
>> On 9/17/19 7:22 AM, Kevin Wolf wrote:
>>> Am 17.09.2019 um 13:07 hat Max Reitz geschrieben:
>>>> On 17.09.19 10:40, Kevin Wolf wrote:
>>>>> Am 17.09.2019 um 10:18 hat Max Reitz geschrieben:
>>>>>> On 13.09.19 20:30, John Snow wrote:
>>>>>>> I'd still like to define func_wrapper with a nod to the type constraint
>>>>>>> it has:
>>>>>>>
>>>>>>> def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
>>>>>>>     [...]
>>>>>>>
>>>>>>>
>>>>>>> Then, you'd write:
>>>>>>>
>>>>>>> if callable(required_formats):
>>>>>>>     fmts = required_formats(instance)
>>>>>>> else:
>>>>>>>     fmts = required_formats
>>>>>>
>>>>>> Yep, that anyway.  (Although I didn’t know about the “param: type”
>>>>>> syntax and put that constraint in a comment instead.  Thanks again :-))
>>>>>
>>>>> Note that function annotations are Python 3 only, so we can't use that
>>>>> syntax yet anyway. If you want to use type hints that are understood by
>>>>> tools (like mypy) and compatible with Python 2, you have to use
>>>>> something like this (feel free to be more specific than Any):
>>>>
>>>> Do we really feel like staying compatible with Python 2, though?
>>>
>>> Feel like it? No.
>>>
>>> It's more that we are compelled to do so because we only deprecated it
>>> in 4.1.
>>
>> Sorry for the impromptu lesson on type hints in 3.5! I added that in to
>> my suggestion as a demonstrative example and didn't mean for you to use
>> it as-is, sorry for not making that clear.
>>
>> I'm confused about the Python3 deprecation timeline. Normally we'd
>> follow our standard approach, but it does hit EOL at the end of this
>> year, so do we drop support then, too? I have the memory of a goldfish I
>> suppose, and can't quite remember our conclusions, if any, of previous
>> discussions on this subject.
> 
> It shouldn't make a difference actually because deprecation in 4.1 means
> that 4.2 (in December) will be the last release that must still support
> Python 2, and we can switch to Python 3 for 5.0.
> 
>> If we do drop python2 though, the new minimum version appears to be 3.5
>> because that's what ships in EPEL. That'd give us standardized type
>> hints that we can use for static analysis tools.
> 
> Actually I seem to remember I suggested that we should make 3.5 the
> minimum Python 3 version, and I thought a patch to this effect had been
> merged, but now I can't find any such check in configure. Maybe I should
> find the old thread again to see if there was any reason not to do this.
> 
> Personally, I would have preferred 3.6 because it brings in variable
> annotations, but I think last time the conclusion was that it would be
> 3.5 indeed.
> 

And with variable annotations you get data classes too, I believe, which
are quite handy.

Oh well.

I have a patch that replaces the configure checks, but there are a few
places in docker and the EDK2 build where we require python2 still, so I
have a few threads to chase down before proposing a patch.

Stay tuned, I guess.

--js
Kevin Wolf Sept. 17, 2019, 2:05 p.m. UTC | #16
Am 17.09.2019 um 15:44 hat John Snow geschrieben:
> 
> 
> On 9/17/19 9:42 AM, Kevin Wolf wrote:
> > Am 17.09.2019 um 15:09 hat John Snow geschrieben:
> >> On 9/17/19 7:22 AM, Kevin Wolf wrote:
> >>> Am 17.09.2019 um 13:07 hat Max Reitz geschrieben:
> >>>> On 17.09.19 10:40, Kevin Wolf wrote:
> >>>>> Am 17.09.2019 um 10:18 hat Max Reitz geschrieben:
> >>>>>> On 13.09.19 20:30, John Snow wrote:
> >>>>>>> I'd still like to define func_wrapper with a nod to the type constraint
> >>>>>>> it has:
> >>>>>>>
> >>>>>>> def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
> >>>>>>>     [...]
> >>>>>>>
> >>>>>>>
> >>>>>>> Then, you'd write:
> >>>>>>>
> >>>>>>> if callable(required_formats):
> >>>>>>>     fmts = required_formats(instance)
> >>>>>>> else:
> >>>>>>>     fmts = required_formats
> >>>>>>
> >>>>>> Yep, that anyway.  (Although I didn’t know about the “param: type”
> >>>>>> syntax and put that constraint in a comment instead.  Thanks again :-))
> >>>>>
> >>>>> Note that function annotations are Python 3 only, so we can't use that
> >>>>> syntax yet anyway. If you want to use type hints that are understood by
> >>>>> tools (like mypy) and compatible with Python 2, you have to use
> >>>>> something like this (feel free to be more specific than Any):
> >>>>
> >>>> Do we really feel like staying compatible with Python 2, though?
> >>>
> >>> Feel like it? No.
> >>>
> >>> It's more that we are compelled to do so because we only deprecated it
> >>> in 4.1.
> >>
> >> Sorry for the impromptu lesson on type hints in 3.5! I added that in to
> >> my suggestion as a demonstrative example and didn't mean for you to use
> >> it as-is, sorry for not making that clear.
> >>
> >> I'm confused about the Python3 deprecation timeline. Normally we'd
> >> follow our standard approach, but it does hit EOL at the end of this
> >> year, so do we drop support then, too? I have the memory of a goldfish I
> >> suppose, and can't quite remember our conclusions, if any, of previous
> >> discussions on this subject.
> > 
> > It shouldn't make a difference actually because deprecation in 4.1 means
> > that 4.2 (in December) will be the last release that must still support
> > Python 2, and we can switch to Python 3 for 5.0.
> > 
> >> If we do drop python2 though, the new minimum version appears to be 3.5
> >> because that's what ships in EPEL. That'd give us standardized type
> >> hints that we can use for static analysis tools.
> > 
> > Actually I seem to remember I suggested that we should make 3.5 the
> > minimum Python 3 version, and I thought a patch to this effect had been
> > merged, but now I can't find any such check in configure. Maybe I should
> > find the old thread again to see if there was any reason not to do this.
> > 
> > Personally, I would have preferred 3.6 because it brings in variable
> > annotations, but I think last time the conclusion was that it would be
> > 3.5 indeed.
> > 
> 
> And with variable annotations you get data classes too, I believe, which
> are quite handy.

I didn't know these. Looks convenient, only in 3.7, though. We might be
there in five years.

Kevin
Max Reitz Sept. 17, 2019, 2:12 p.m. UTC | #17
On 17.09.19 13:22, Kevin Wolf wrote:
> Am 17.09.2019 um 13:07 hat Max Reitz geschrieben:
>> On 17.09.19 10:40, Kevin Wolf wrote:
>>> Am 17.09.2019 um 10:18 hat Max Reitz geschrieben:
>>>> On 13.09.19 20:30, John Snow wrote:
>>>>> I'd still like to define func_wrapper with a nod to the type constraint
>>>>> it has:
>>>>>
>>>>> def func_wrapper(instance: iotests.QMPTestCase, *args, **kwargs):
>>>>>     [...]
>>>>>
>>>>>
>>>>> Then, you'd write:
>>>>>
>>>>> if callable(required_formats):
>>>>>     fmts = required_formats(instance)
>>>>> else:
>>>>>     fmts = required_formats
>>>>
>>>> Yep, that anyway.  (Although I didn’t know about the “param: type”
>>>> syntax and put that constraint in a comment instead.  Thanks again :-))
>>>
>>> Note that function annotations are Python 3 only, so we can't use that
>>> syntax yet anyway. If you want to use type hints that are understood by
>>> tools (like mypy) and compatible with Python 2, you have to use
>>> something like this (feel free to be more specific than Any):
>>
>> Do we really feel like staying compatible with Python 2, though?
> 
> Feel like it? No.
> 
> It's more that we are compelled to do so because we only deprecated it
> in 4.1.

Hm, yes, that’s too bad. :-)

Max
diff mbox series

Patch

diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093
index 50c1e7f2ec..f03fa24a07 100755
--- a/tests/qemu-iotests/093
+++ b/tests/qemu-iotests/093
@@ -24,7 +24,7 @@  import iotests
 nsec_per_sec = 1000000000
 
 class ThrottleTestCase(iotests.QMPTestCase):
-    test_img = "null-aio://"
+    test_driver = "null-aio"
     max_drives = 3
 
     def blockstats(self, device):
@@ -35,10 +35,14 @@  class ThrottleTestCase(iotests.QMPTestCase):
                 return stat['rd_bytes'], stat['rd_operations'], stat['wr_bytes'], stat['wr_operations']
         raise Exception("Device not found for blockstats: %s" % device)
 
+    def required_drivers(self):
+        return [self.test_driver]
+
+    @iotests.skip_if_unsupported(required_drivers)
     def setUp(self):
         self.vm = iotests.VM()
         for i in range(0, self.max_drives):
-            self.vm.add_drive(self.test_img, "file.read-zeroes=on")
+            self.vm.add_drive(self.test_driver + "://", "file.read-zeroes=on")
         self.vm.launch()
 
     def tearDown(self):
@@ -264,7 +268,7 @@  class ThrottleTestCase(iotests.QMPTestCase):
         self.assertEqual(self.blockstats('drive1')[0], 4096)
 
 class ThrottleTestCoroutine(ThrottleTestCase):
-    test_img = "null-co://"
+    test_driver = "null-co"
 
 class ThrottleTestGroupNames(iotests.QMPTestCase):
     max_drives = 3
@@ -425,4 +429,6 @@  class ThrottleTestRemovableMedia(iotests.QMPTestCase):
 
 
 if __name__ == '__main__':
+    if 'null-co' not in iotests.supported_formats():
+        iotests.notrun('null-co driver support missing')
     iotests.main(supported_fmts=["raw"])