You can get rid of the import string; pyflakes tells me that it's not used.  
Other pyflakes warnings include:

In Job.instances(), the 'remote_object' local variable is not used.  However, I 
see in the very next line that you're passing in self.remote_object as the 
first argument to dbus.Interface().  Perhaps that should have just been 
'remote_object'?

In SessionInit.__init__(), the 'watch' variable is assigned to but never used.  
Maybe just get rid of the assignment?

In TestUpstart.test_session_init_reexec(), the fd variable is assigned to but 
never used.  I'm not sure what the intent is for this one.

Other comments:

* wait_for_file() seems racy.  Also, it's not doing exactly what it claims 
because a time.sleep(1) because that call can sleep more or less than a second 
based on various factors.  When I want to do more accurate time based waits, I 
use a loop like this:

from datetime import datetime, timedelta
...

    until = datetime.now() + timedelta(5)
    while datetime.now() < until:
        if do_check():
            return True
        time.sleep(0.1)
    return False

You can of course tweak the details, but the basic idea is that you set up a 
point in the future which you'll check until and then the loop exit condition 
looks to see if *now* is past that future time.

Although it doesn't hurt, it's mildly unpythonic to use parens with an empty 
base class list, e.g. "class Upstart()".  We usually omit the parens (I think 
the empty base class syntax was actually illegal in Python 2 ;).

PEP 8 wants lines less than 79 characters wide.  You have a few lines that are 
too long.  Suggestions for wrapping include, instead of:

        self.remote_object = self.connection.get_object(object_path=OBJECT_PATH)

use

        self.remote_object = self.connection.get_object(
            object_path=OBJECT_PATH)

and instead of

        raise UpstartException('Failed to reconnect to Upstart after %d 
seconds' % timeout)

use

        raise UpstartException(
            'Failed to reconnect to Upstart after %d seconds' % timeout)

Remember that no backslash is necessary inside parens/braces/brackets!

In polling_connect(), you have a bare-except.  Generally you should avoid such 
bare-excepts because they can and do mask all sorts of exceptions you aren't 
expecting.  Better to narrow that down to exactly the set of exceptions you 
expect and want to ignore from self.connect().  (Aside: Python 3.4 will have a 
very cool contextlib.ignored() helper to ignore specific sets of exceptions.  
Doesn't help you now though ;).

In general, current best practices these days is to avoid 
double-leading-underscore attributes such as __timeout_cb() and 
__idle_create_job_cb().  Python does name mangling on double-leading-underscore 
names, which can make debugging and introspection more difficult.  DLU 
attributes should only be used where potential namespace collisions with 
subclasses are a possibility (i.e. as part of a "protected" API for 
subclasses).  In most of these cases, a single leading underscore is enough to 
say "hey, this is my private stuff, don't use it!" without the inconvenience of 
name mangling.

In __idle_create_job_cb(), since at least two arguments are required (in 
addition to self), you can define the method like so:

def __idle_create_job_cb(self, name, body, *args)

and just ignore 'args'.  Then you don't need to explicitly unpack args[0] and 
args[1].

In __job_added_cb(), you don't need parens around the arguments to assert.  
It's a statement, not a function in Python.  It's also usually a good idea to 
add a more meaningful error message as the second expression for assert, e.g.

    assert self.timeout_source, 'Why is there no timeout source? 
{}'.format(self.timeout_source)

Asserts also get compiled away when __debug__ is False, or when python3 -OO is 
used.

.get_test_dir() isn't used anywhere, and since it's not a property, it doesn't 
really save you anything over just accessing self.test_dir directly.

In create_dirs(), it's usually better (and less racy!) to try to create the 
directory and catch the FileExistsError that can result if the directory 
already exists.

In destroy(), os.rmdir() will fail if the directory is not empty.  Use 
shutil.rmtree() to really zap the whole directory and all its contents.

In emit(), don't set the env=[] parameter this way.  This is a subtle gotcha in 
Python - the parameter's default value is set when the method is defined, *not* 
when it's called.  If for example, dbus.Array() modified 'env', that would 
affect all future callers of emit.  Try running this code to see it in action:

def foo(env=[]):
    print(env)
    env.append('yes ')

foo()
foo()
foo()


:).  Much better to:

def emit(self, event, env=None, wait=True):
    if env is None:
        env = []

to ensure that env always gets a fresh empty list by default.

In version(), don't compare "raw == True".  We almost never explicitly compare 
against True and False, and never use == to compare against singletons.  "if 
raw:" is usually good enough.  Also in this method, you probably don't need the 
extra set of parens around the return value.  E.g. this should do the trick:

return version_string.split()[2].strip(')')

Yikes!  You have mixed tabs and spaces in reexec().  Python will barf on that.  
See lines 300-301.

You might want to think about using ''.format() instead of % interpolation.  I 
think it leads to more readable code, and it definitely allows you to avoid 
backslashes, which seem ugly to me ;).  E.g. instead of

self.job_object_path = '%s/%s/%s' % \
    (OBJECT_PATH, 'jobs', dbus_encode(job_path))

use

self.job_object_path = '{}/{}/{}'.format(OBJECT_PATH, 'jobs', 
dbus_encode(job_path))

(and actually, in the first case, if you move the open paren to the end of the 
first line, you won't need the backslash anyway.  But still, I love .format() :)

On lines 355-357, better to indent the arguments to 
self.connection.add_signal_receiver() by 4 spaces.

Line 364 has another comparison using equality to a boolean.  Better to just 
use "if not job.seen:".

Line 422 could use this instead: "self.conffile = os.path.join(self.job_dir, 
self.name + '.conf')"

There are a few things about the code that writes to conffile that bug me.  The 
first is that you should use a with-statement to ensure that the file gets 
closed when you're done with it.  Second, by default open(..., 'w') will open 
the file in text mode, but you're not defining an encoding, and Python will use 
a platform-dependent value by default.  This will probably work fine until it 
doesn't and then it'll be a PITA to debug. ;).  Best to be explicit.  E.g. if 
you want the conf file to be a text file (i.e. instead of bytes), then open it 
with a specific encoding, probably utf-8.  Lastly, since body can be a list of 
strings or a string, why not splitlines() the string value and then treat them 
the same.  All together, I'd probably rewrite this code like so:

if isinstance(body, str):
    # Assume body cannot be a bytes object.
    body = body.splitlines()
with open(self.conffile, 'w', encoding='utf-8') as fh:
    for line in body:
        print(line.strip(), file=fh)
    print(file=fh)

This is untested, but I think you get the idea!

Maybe clean up the long lines and backslashes on lines 436-440.

The bare-except in destroy() also bugs me.  It also makes no sense to use a 
finally: statement with a bare-except, since all exceptions will get caught 
(and in this case ignored) so the finally is kind of useless.

def start() has another mutable function argument.

In _get_instance(), you know that with the default arguments, you could end up 
with an object_path like 'foo/None' right?

The backslash in instance_object_paths() is unnecessary.  You should also 
indent the 'for' to the character after the open brace.

In pids(), name will be '_' under more conditions of falseness that perhaps you 
expect.  Probably better to write this as a ternary statement, e.g.:

    name = ('_' if name is None else name)

(the parens are not strictly necessary here.  They are my one bow to 
readability, unlike the unnecessary parens for the assert statement :)

In method running(), you actualy don't need the ternary statement!  "return 
len(self.pids(name)) > 0" will do the trick. :)

In logfile_name(), probably better to use "logfile = 
os.path.join(self.upstart.log_dir, filename)"

Okay, that's 50% of the file.  To keep these manageable, I'll do the rest of 
the review in a second comment...
-- 
https://code.launchpad.net/~jamesodhunt/upstart/python-upstart-module/+merge/157549
Your team Upstart Reviewers is requested to review the proposed merge of 
lp:~jamesodhunt/upstart/python-upstart-module into lp:upstart.

-- 
upstart-devel mailing list
[email protected]
Modify settings or unsubscribe at: 
https://lists.ubuntu.com/mailman/listinfo/upstart-devel

Reply via email to