diff mbox series

[09/22] qapi: add match_nofail helper

Message ID 20210422030720.3685766-10-jsnow@redhat.com
State New
Headers show
Series qapi: static typing conversion, pt5a | expand

Commit Message

John Snow April 22, 2021, 3:07 a.m. UTC
Mypy cannot generally understand that these regex functions cannot
possibly fail. Add a _nofail helper that clarifies this for mypy.
Signed-off-by: John Snow <jsnow@redhat.com>
---
 scripts/qapi/common.py |  8 +++++++-
 scripts/qapi/main.py   |  6 ++----
 scripts/qapi/parser.py | 13 +++++++------
 3 files changed, 16 insertions(+), 11 deletions(-)

Comments

Markus Armbruster April 25, 2021, 7:54 a.m. UTC | #1
John Snow <jsnow@redhat.com> writes:

> Mypy cannot generally understand that these regex functions cannot
> possibly fail. Add a _nofail helper that clarifies this for mypy.

Convention wants a blank line here.

> Signed-off-by: John Snow <jsnow@redhat.com>
> ---
>  scripts/qapi/common.py |  8 +++++++-
>  scripts/qapi/main.py   |  6 ++----
>  scripts/qapi/parser.py | 13 +++++++------
>  3 files changed, 16 insertions(+), 11 deletions(-)
>
> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
> index cbd3fd81d36..d38c1746767 100644
> --- a/scripts/qapi/common.py
> +++ b/scripts/qapi/common.py
> @@ -12,7 +12,7 @@
>  # See the COPYING file in the top-level directory.
>  
>  import re
> -from typing import Optional, Sequence
> +from typing import Match, Optional, Sequence
>  
>  
>  #: Magic string that gets removed along with all space to its right.
> @@ -210,3 +210,9 @@ def gen_endif(ifcond: Sequence[str]) -> str:
>  #endif /* %(cond)s */
>  ''', cond=ifc)
>      return ret
> +
> +
> +def match_nofail(pattern: str, string: str) -> Match[str]:
> +    match = re.match(pattern, string)
> +    assert match is not None
> +    return match

Name it must_match()?  You choose.

I wish we could have more stating typing with less notational overhead,
but no free lunch...

[...]
John Snow April 26, 2021, 5:48 p.m. UTC | #2
On 4/25/21 3:54 AM, Markus Armbruster wrote:
> John Snow <jsnow@redhat.com> writes:
> 
>> Mypy cannot generally understand that these regex functions cannot
>> possibly fail. Add a _nofail helper that clarifies this for mypy.
> 
> Convention wants a blank line here.
> 

Tooling failure.

stg pop -a
while stg push; and stg edit --sign; done

(Will fix, but not so sure about fixing the tool...)

>> Signed-off-by: John Snow <jsnow@redhat.com>
>> ---
>>   scripts/qapi/common.py |  8 +++++++-
>>   scripts/qapi/main.py   |  6 ++----
>>   scripts/qapi/parser.py | 13 +++++++------
>>   3 files changed, 16 insertions(+), 11 deletions(-)
>>
>> diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
>> index cbd3fd81d36..d38c1746767 100644
>> --- a/scripts/qapi/common.py
>> +++ b/scripts/qapi/common.py
>> @@ -12,7 +12,7 @@
>>   # See the COPYING file in the top-level directory.
>>   
>>   import re
>> -from typing import Optional, Sequence
>> +from typing import Match, Optional, Sequence
>>   
>>   
>>   #: Magic string that gets removed along with all space to its right.
>> @@ -210,3 +210,9 @@ def gen_endif(ifcond: Sequence[str]) -> str:
>>   #endif /* %(cond)s */
>>   ''', cond=ifc)
>>       return ret
>> +
>> +
>> +def match_nofail(pattern: str, string: str) -> Match[str]:
>> +    match = re.match(pattern, string)
>> +    assert match is not None
>> +    return match
> 
> Name it must_match()?  You choose.
> 

If you think it reads genuinely better, sure.

> I wish we could have more stating typing with less notational overhead,
> but no free lunch...
> 
> [...]
>
diff mbox series

Patch

diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py
index cbd3fd81d36..d38c1746767 100644
--- a/scripts/qapi/common.py
+++ b/scripts/qapi/common.py
@@ -12,7 +12,7 @@ 
 # See the COPYING file in the top-level directory.
 
 import re
-from typing import Optional, Sequence
+from typing import Match, Optional, Sequence
 
 
 #: Magic string that gets removed along with all space to its right.
@@ -210,3 +210,9 @@  def gen_endif(ifcond: Sequence[str]) -> str:
 #endif /* %(cond)s */
 ''', cond=ifc)
     return ret
+
+
+def match_nofail(pattern: str, string: str) -> Match[str]:
+    match = re.match(pattern, string)
+    assert match is not None
+    return match
diff --git a/scripts/qapi/main.py b/scripts/qapi/main.py
index 70f8aa86f37..e8d4ba4b389 100644
--- a/scripts/qapi/main.py
+++ b/scripts/qapi/main.py
@@ -8,11 +8,11 @@ 
 """
 
 import argparse
-import re
 import sys
 from typing import Optional
 
 from .commands import gen_commands
+from .common import match_nofail
 from .error import QAPIError
 from .events import gen_events
 from .introspect import gen_introspect
@@ -22,9 +22,7 @@ 
 
 
 def invalid_prefix_char(prefix: str) -> Optional[str]:
-    match = re.match(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', prefix)
-    # match cannot be None, but mypy cannot infer that.
-    assert match is not None
+    match = match_nofail(r'([A-Za-z_.-][A-Za-z0-9_.-]*)?', prefix)
     if match.end() != len(prefix):
         return prefix[match.end()]
     return None
diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py
index f2425c0228a..7f3c009f64b 100644
--- a/scripts/qapi/parser.py
+++ b/scripts/qapi/parser.py
@@ -18,6 +18,7 @@ 
 import os
 import re
 
+from .common import match_nofail
 from .error import QAPISemError, QAPISourceError
 from .source import QAPISourceInfo
 
@@ -235,8 +236,8 @@  def accept(self, skip_comment=True):
             elif not self.tok.isspace():
                 # Show up to next structural, whitespace or quote
                 # character
-                match = re.match('[^[\\]{}:,\\s\'"]+',
-                                 self.src[self.cursor-1:])
+                match = match_nofail('[^[\\]{}:,\\s\'"]+',
+                                     self.src[self.cursor-1:])
                 raise QAPIParseError(self, "stray '%s'" % match.group(0))
 
     def get_members(self):
@@ -369,7 +370,7 @@  def append(self, line):
             # Strip leading spaces corresponding to the expected indent level
             # Blank lines are always OK.
             if line:
-                indent = re.match(r'\s*', line).end()
+                indent = match_nofail(r'\s*', line).end()
                 if indent < self._indent:
                     raise QAPIParseError(
                         self._parser,
@@ -505,7 +506,7 @@  def _append_args_line(self, line):
             # from line and replace it with spaces so that 'f' has the
             # same index as it did in the original line and can be
             # handled the same way we will handle following lines.
-            indent = re.match(r'@\S*:\s*', line).end()
+            indent = match_nofail(r'@\S*:\s*', line).end()
             line = line[indent:]
             if not line:
                 # Line was just the "@arg:" header; following lines
@@ -540,7 +541,7 @@  def _append_features_line(self, line):
             # from line and replace it with spaces so that 'f' has the
             # same index as it did in the original line and can be
             # handled the same way we will handle following lines.
-            indent = re.match(r'@\S*:\s*', line).end()
+            indent = match_nofail(r'@\S*:\s*', line).end()
             line = line[indent:]
             if not line:
                 # Line was just the "@arg:" header; following lines
@@ -586,7 +587,7 @@  def _append_various_line(self, line):
             # from line and replace it with spaces so that 'f' has the
             # same index as it did in the original line and can be
             # handled the same way we will handle following lines.
-            indent = re.match(r'\S*:\s*', line).end()
+            indent = match_nofail(r'\S*:\s*', line).end()
             line = line[indent:]
             if not line:
                 # Line was just the "Section:" header; following lines