All of lore.kernel.org
 help / color / mirror / Atom feed
From: John Snow <jsnow@redhat.com>
To: Markus Armbruster <armbru@redhat.com>
Cc: "Marc-André Lureau" <marcandre.lureau@redhat.com>,
	"Cleber Rosa" <crosa@redhat.com>,
	qemu-devel@nongnu.org, "Eduardo Habkost" <ehabkost@redhat.com>
Subject: Re: [PATCH 06/12] qapi/source: Add builtin null-object sentinel
Date: Fri, 18 Dec 2020 14:14:23 -0500	[thread overview]
Message-ID: <1dac428c-6cd4-5922-0f0d-7aa37a0ecba8@redhat.com> (raw)
In-Reply-To: <87a6uck3y4.fsf@dusky.pond.sub.org>

On 12/17/20 7:33 AM, Markus Armbruster wrote:
> A patch limited to the first aspect merely tweaks an implementation
> detail.
> 
> As soon as we include the second aspect, we get to debate how to handle
> programming errors, and maybe whether any of the errors involving a
> QAPISourceInfo.builtin() are*not*  programming errors.
> 
> I'd prefer this series to remain focused on "enabling strict optional
> checking in mypy for everything we have typed so far".

That is still the focus -- I believe adding a poison pill as you suggest 
actually causes more errors instead of "maintaining" them; it doesn't 
maintain a status quo.

Now, I understand the concern that this is opening a chance to allow a 
new class of errors to slip by undetected, but I don't believe it does 
in a meaningful way.

I didn't intend for this info object to be a poison; it wasn't designed 
as one, nor should it be. It's merely a SourceInfo that represents the 
degenerate case that something-or-other does not have an explicit user 
source.

You're right that it probably shouldn't be seen in normal conditions, 
but if it has; my take is that something *else* has already gone 
horribly wrong and we aren't just sweeping dust under the mat, actually.


Here's a recap of my thinking process in identifying the problem and my 
fix for it:

- Many functions expect that QAPISourceInfo *will* be present. Largely, 
they are correct in expecting this, but very few of them check or assert 
this. This is perfectly normal for untyped Python.

- Taxonomically, however, this is untrue, because the built-in entities 
do not need to specify one.

- This can be addressed in typed python by adding assertions everywhere 
("assert info is not None"), or it can be addressed by making the core 
assumption true ("QAPISourceInfo is not Optional").

I opted to make the core assumption true; which does introduce a new 
ambiguity that may not have been present before:

"If we have an info object, is it a real user-source info object, or is 
it the built-in sentinel?"

I *think* this is where you start to get worried; we have "lost" the 
ability to know, in an "obvious" way if this object is "valid" for a 
given context. The assumption is that the code will explode violently if 
we are passed None instead of a valid source info.

I don't think this assumption is true, though. mypy informs me that we 
were never checking to see if it was valid anyway, so we already have 
this ambiguity in the code today. Given that the 'info' object is, in 
most cases, not used until *after we have detected another error*, 
changing the info object to be non-none does not suppress any errors 
that didn't already exist in these contexts.

What about places where QAPISourceInfo is used outside of an 
error-handling context to gain contextual information about an entity? 
Do we open ourselves up to new cases where we may have had an implicit 
error, but now we do not?


Explicit usages of QAPISourceInfo are here:

1. QAPISchemaEntity._set_module(), which uses the info fname to find the 
QAPISchemaModule object. This already checks for "None" info now, and 
will check for the "./builtin" info after the series.

2. QAPISchemaEntity.is_implicit(), which uses the falsey nature of info 
to intuit that it is a built-in definition. No change in behavior.

3. QAPISchemaArrayType.check(), which uses the falsey nature of 
self.info to conditionally pass self.info.defn_meta to 
QAPISchema.resolve_type().

Found a bug (previously fixed in part 6 prior to enabling type checking 
on schema.py, which is why it went undetected here. I am moving the fix 
forward into this series):

info and info.defn_meta will evaluate to the sentinel QAPISourceInfo 
instead of an empty string or None, but it can be changed simply to just 
"info.defn_meta" to take the None value from the builtin source info's 
defn_meta field; easy.

4. QAPISchemaMember.describe(), which uses info.defn_name. This is only 
ever called in error-handling contexts, so we aren't reducing total 
error coverage here, either.

5. QAPISchemaCommand.check(), which uses info.pragma.returns_whitelist. 
This might legitimately create a new avenue where we succeed where we 
used to fail, if we were to create a built-in command. Whether or not 
this is desirable depends on how you want pragma directives to work 
long-term; per-file or per-schema. (Do you want to let user-defined 
schemas dictate how built-in commands work?)

I don't think this is an issue.

6. QAPISchema._def_entity(), which asserts that info must be true-ish, 
or we are in the predefining state. This logic doesn't change.

7. QAPISchema._def_entity(), which uses the true-ish nature of 
other_ent.info to print a different error message. This logic doesn't 
change.

8. expr.py:check_type(), which uses info.pragma.returns_whitelist. This 
theoretically adds slack, but parser.py always gives us real info objects.

9. expr.py:check_exprs(), which calls info.set_defn(meta, name). 
Obviously this must not be None because parser.py does not create 
"built-in" source objects; we know with certainty here that we cannot 
get such a thing.

10. expr.py:check_exprs() again, checking info.pragma.doc_required. As 
above, we know this is always a true source info.

11. QAPIError's str() method itself will call str(info), but if info is 
None, it doesn't crash -- you just get "None". Now you'll get 
"[builtin]". Doesn't permit any new error cases.



Conclusions:

- There are users that use info to decide on behavior outside of an 
error context, but they are already cared for.

- There are some pre-schema.py uses of info that may cause less problems 
than they used to, but would require someone to add the placeholder 
object into parser.py. We can throw a single assertion in expr.py to 
make sure it never happens by accident.

- There are no, and I doubt there ever will be, users who use str(info) 
to decide on behavior. There are only exception handlers that use this 
method. There is no value to making str(info) in particular a poison pill.

- There might be some limited benefit to poisoning info.pragma, depending.


Ultimately, I consider this the standard idiomatic usage of info:

```
def some_func(info):
     if (error):
         raise QAPISemError(info, "you forgot to defrobulate")
```

When we go to print the error message to the user, the exception handler 
is going to fault because str(info) is poisoned. Is it useful to poison 
the exception handler here?

I don't think so; especially considering that even "None" here will work 
just fine!


So I suppose I would prefer not to add poison, because I don't think 
it's necessary, or improves anything materially.

--js



  reply	other threads:[~2020-12-18 19:16 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-14 23:53 [PATCH 00/12] qapi: static typing conversion, pt1.5 John Snow
2020-12-14 23:53 ` [PATCH 01/12] qapi/commands: assert arg_type is not None John Snow
2020-12-14 23:53 ` [PATCH 02/12] qapi/events: fix visit_event typing John Snow
2020-12-14 23:53 ` [PATCH 03/12] qapi/main: handle theoretical None-return from re.match() John Snow
2020-12-16  8:23   ` Markus Armbruster
2020-12-16 17:11     ` John Snow
2020-12-14 23:53 ` [PATCH 04/12] qapi/gen: assert that _start_if is not None in _wrap_ifcond John Snow
2020-12-16  8:26   ` Markus Armbruster
2020-12-16 17:13     ` John Snow
2020-12-17  7:24       ` Markus Armbruster
2020-12-14 23:53 ` [PATCH 05/12] qapi/gen: use './builtin' for the built-in module name John Snow
2020-12-16  8:35   ` Markus Armbruster
2020-12-16 17:27     ` John Snow
2020-12-14 23:53 ` [PATCH 06/12] qapi/source: Add builtin null-object sentinel John Snow
2020-12-16  9:22   ` Markus Armbruster
2020-12-16 17:53     ` John Snow
2020-12-17 12:33       ` Markus Armbruster
2020-12-18 19:14         ` John Snow [this message]
2020-12-16 19:11     ` John Snow
2020-12-17 11:56       ` Markus Armbruster
2020-12-18 19:22         ` John Snow
2020-12-14 23:53 ` [PATCH 07/12] qapi/gen: write _genc/_genh access shims John Snow
2020-12-14 23:53 ` [PATCH 08/12] qapi/schema: make QAPISourceInfo mandatory John Snow
2020-12-16 10:18   ` Markus Armbruster
2020-12-16 18:41     ` John Snow
2020-12-17  8:02       ` Markus Armbruster
2020-12-17 17:02         ` John Snow
2020-12-18  5:24           ` Markus Armbruster
2020-12-18 19:17             ` John Snow
2020-12-18 20:57               ` Markus Armbruster
2020-12-18 21:30                 ` John Snow
2020-12-14 23:53 ` [PATCH 09/12] qapi/gen: move write method to QAPIGenC, make fname a str John Snow
2020-12-16 10:31   ` Markus Armbruster
2020-12-14 23:53 ` [PATCH 10/12] tests/qapi-schema: Add quotes to module name in test output John Snow
2020-12-14 23:53 ` [PATCH 11/12] qapi/schema: Name the builtin module "" instead of None John Snow
2020-12-16 10:42   ` Markus Armbruster
2020-12-16 18:57     ` John Snow
2020-12-17 11:09       ` Markus Armbruster
2020-12-17 21:07         ` John Snow
2020-12-18  5:31           ` Markus Armbruster
2020-12-14 23:53 ` [PATCH 12/12] qapi: enable strict-optional checks 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=1dac428c-6cd4-5922-0f0d-7aa37a0ecba8@redhat.com \
    --to=jsnow@redhat.com \
    --cc=armbru@redhat.com \
    --cc=crosa@redhat.com \
    --cc=ehabkost@redhat.com \
    --cc=marcandre.lureau@redhat.com \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.