[pkg-apparmor] UDD notifications script

u u at 451f.org
Mon Feb 23 11:29:13 UTC 2015


Hi,

thank you both Christian and intrigeri for the valuable input.
I'm having a lot of fun implementing the corrections :)

intrigeri:
> 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! :)

> * 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?

Done, using Christian's suggestion.

> * 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 :)

I finally did that by joining the two tables and it the query still
seems to be done quite quickly.

> * 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.

I will run this on the new script too.

> * 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.

Tried that in a previous iteration and i worked. But I dropped it since
as I don't need it anymore.

>   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 :)

OK. Corrected.

> * 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 :)

Ok. Corrected.

> * 'print "No new data."' might produce useless noise when run from
>   cron, no?

Deleted.

> * 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.

Indeed.

> * The send_team_notification function still uses the sender and
>   receiver global variables. Any reason why?

I modified this. I kept them global in first place because the other
option (which I am using now) needs to pass about 5 parameters to that
function.

> * 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.

Indeed.

I was not very firm with saving the data to a file and thus ended up
with a weird converstion from a saved string to a set in first place.
But now I've seen that the pickle module can save and read dictionaries
just fine and changed the way I save the data. Thus, I can now use those
instead.

> * 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? :)

Your newbie PoV is right :)
As we use the dictionary list now, this has become obsolete.

> * The "Send the message via our local SMTP server" comment feels
>   wrong, now that you've made the SMTP relay configurable.

Corrected.

u.



More information about the pkg-apparmor-team mailing list