[pkg-apparmor] UDD notifications script
intrigeri
intrigeri at debian.org
Thu Feb 19 18:37:18 UTC 2015
Hi,
u wrote (19 Feb 2015 17:20:52 GMT) :
> I think I've applied everything you mentioned and share your vision.
Yay, congrats, I find it a lot more readable and cleaner. Thanks! :)
Want more? Here are a few more comments. Again, feel free to tell me
"now that's enough, thank you" or "patches welcome", about some or all
of the following comments.
* This looks suboptimal (but of course, as long as we haven't
a massive amount of usertagged bugs, it should be just fine):
for bug in cursor.fetchall():
buglist.append(bug)
... so I'm curious: can't we do that in one batch operation instead?
* When using the get_bug_list output (a list of pairs) in
send_team_notification, we have to access it by indices: bug[0] to
get the bug ID, bug[1] to get the added or removed usertag. It's not
astonishingly obvious. I would recommend that get_bug_list outputs:
- either a list of dictionaries so we can then write bug['id']
and bug['usertag']
- or a dictionary whose keys would be the bug IDs, and values would
be the usertag; but wait! this would be too limiting, read on:
* I see we first retrieve a list of usertagged bugs (in get_bug_list),
and then run one more query per bug that had a usertag added or
removed, in order to get its title. Alternatively, get_bug_list
could use a JOIN to retrieve all the data we need about all
usertagged bugs in one go. No idea the higher number of queries
(current implementation) costs more than retrieving the title of
bugs that we won't use (suggested implementation). Anyhow, putting
aside performance and resources considerations, I would find the
implementation of send_team_notification more readable and elegant
if get_bug_list returned a list of dictionaries, each such
dictionary having keys (id, title, usertag). Food for thought :)
* pylint says that "def get_bug_title(bugid) :" has one too much
space. It's also quite unhappy about a lot of other small things.
Possibly it knows Python best practices better than you and I.
I've no idea (really -- it might produce obsolete recommendations,
and I've not checked). Similarly, pyflakes is a bit mean and detects
two unused variables.
* In:
cursor.execute("SELECT title from bugs WHERE id='%s'" % bugid)
for bug in cursor.fetchall():
return bug[0]
... we're somewhat acting defensively as if we didn't have
a guarantee that the id is a unique identifier (is it the case?),
which can arguably be a good thing.
But we're actually not defensive in practice, since we're always
returning the title of the first matching bug and ignoring any
other. So, retrieving all the data using fetchall(), and then
iterating on the first result only in a for loop feels... weird.
So I would simply use fetchone() instead of fetchall(), and drop the
for loop entirely.
Now, if you really want to be more defensive, we can also count the
number of matching bugs and throw an exception if it's 2 or more,
but I suspect we could avoid that after a quick look at the
UDD schema. What's an ID if it's not an ID? :)
* Mixing tabs and spaces for indentation produces very confusing
results in my $EDITOR:
except IOError as e:
send_error_mail("Could not save state file.")
return False
return True
... here, the "return False" statement is really part of the
exception handling block, even if it doesn't look like it when one
has tabs display width = 8 spaces. I suggest applying the
indentation changes suggested by pylint to avoid that :)
* In the read_statefile function, I believe that the "return old"
statement has no good reason to be in the try block. In general it's
good practice to guard as little code as possible with exception
catching blocks. In this case, as a non-expert in Python, I have no
idea if it may have any adversarial consequences in practice to
leave it there, but IMO it would be good code hygiene to move it
after the exception handling block. And by the way, it'll make it
clearer what is the expected output of the function on success :)
* 'print "No new data."' might produce useless noise when run from
cron, no?
* The 'else' on line 93 (at commit 40226e0) forces us to indent the
main algorithm of this function. I generally prefer dropping such
else clauses, and instead return earlier in case we cannot do our
job... which is actually already done in this function, so the
refactoring I'm suggesting is trivial.
* The send_team_notification function still uses the sender and
receiver global variables. Any reason why?
* It seems that we're passing to compare_state:
- a string we got from read_statefile
- a list we got from get_bug_list
... and then compare_state treats both as strings. For some reason,
it works anyway.
I think I'd find the design cleaner if compare_state took structured
data as input (a set, a dictionary, whatever it prefers). And then,
read_statefile should handle deserialization and return data in that
same format. And then, we would be handling {,}deserialization only
in save_statefile and read_statefile, and using only structured data
anywhere else, which feels better to me.
* compare_state has a comment that says "convert new state string to
dictionary", while it's apparently (from my Python almost-newbie
PoV) creating a Set object instead. Who's right? :)
* The "Send the message via our local SMTP server" comment feels
wrong, now that you've made the SMTP relay configurable.
Cheers!
--
intrigeri
More information about the pkg-apparmor-team
mailing list