Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make is_active for projector return quickly #326

Merged
merged 14 commits into from
Aug 8, 2017
Merged

Conversation

chihuahua
Copy link
Member

This change makes the projector plugin start a separate thread to compute whether it is active. This allows the is_active method to return immediately. Otherwise, it may take a long time to return, which could cause issues. See #301.

We can actually compute the _is_active property in less time by halting the other thread when we know we have 1 entry in the config object (instead of computing all the values of the object), but computing the config object should usually not take severely long anyway so the added complexity and duplicated logic does not seem worth it.

This change makes the projector plugin start a separate thread to compute whether it is active. This allows the `is_active` method to return immediately. Otherwise, it may take a long time to return, which could cause issues - see #301

We can actually compute the _is_active property in less time by halting the other thread when we know we have 1 entry in the config object (instead of computing all the values of the object), but computing the config object should usually not take severely long anyway so the added complexity and duplicated logic does not seem worth it.
def _get_thread_for_determining_is_active(self):
"""Gets the currently running thread for determining is_active.

This method is useful for testing.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? This method is only used in tests, and all of its uses can be replaced with the direct field access. (They're both private, so it's not like using a method is more principled!)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. Done.

tf.test.mock.patch('threading.Thread', FakeThread).start()

# The projector plugin has not yet determined whether it is active, but it
# should have started a thread to determine that.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Should have started" should read "should now start".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

# Embedding data is available.
# The is_active method makes use of a separate thread, so we mock threading
# behavior to make this test deterministic.
tf.test.mock.patch('threading.Thread', FakeThread).start()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole stubbing business is far more complicated than it needs to be. Why not just:

patcher = tf.test.mock.patch('threading.Thread.start', autospec=True)
mock = patcher.start()
self.addCleanup(patcher.stop)  # remove `tearDown`

mock.assert_not_called()
self.assertFalse(self.plugin.is_active())
mock.assert_called_once_with(self.plugin._thread_for_determining_is_active)

self.assertFalse(self.plugin.is_active())
mock.assert_called_once_with(self.plugin._thread_for_determining_is_active)

self.plugin._thread_for_determining_is_active.run()
self.assertTrue(self.plugin.is_active())

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the one hurdle here is that the mock doesn't actually run the logic of the thread, so self.assertTrue(self.plugin.is_active()) fails. Do you know of a way around that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes: call thread.run(), as in the snippet above. This will invoke the thread's handler synchronously.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works and removes the complexity of FakeThread. Wonderful idea.

1 alteration I made was replace mock.assert_not_called() with self.assertEqual(0, mock.call_count) because TF's version of mock lacked assert_not_called.

@@ -250,7 +261,49 @@ def is_active(self):
Returns:
A boolean. Whether this plugin is active.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's clear that is_active returns whether the plugin is active. This docstring should indicate what that means. ("The projector plugin is active when…")

Copy link
Contributor

@wchargin wchargin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two aspects of the logic that aren't tested and would be fairly easy to test: (1) if the plugin is found to be active, further calls to is_active do not start a new thread; (2) if the plugin is found to be inactive, further calls to is_active do spawn a new thread. I'd recommend including these tests, but it's up to you, so I'm approving to unblock you.

mock = patcher.start()
self.addCleanup(patcher.stop)

self.assertEqual(0, mock.call_count)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is pretty useless: there's no chance for anyone to call threading.Thread.start between the time that you start the mock and this check. Might as well remove.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@chihuahua
Copy link
Member Author

+1, we now test those 2 cases.

@chihuahua chihuahua merged commit e122877 into master Aug 8, 2017
@chihuahua chihuahua deleted the projector_is_active branch August 8, 2017 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants