How Tests Can Help You Migrate Your Code Between Different Python Versions
After meeting some of the main differences between Python 2 and 3, this lesson shows you how tests can help you migrating your Python code to another version.
In this case a feature of the datetime
module introduced in Python 2.7 is tested and backported to Python 2.6.
00:00 So, let’s go back to Travis and see what the issues are that we’re seeing when building against Python 3.2 and 2.6. We’ll first take a look at 2.6 and see what could be the problem.
00:12
This shows us the build output from Travis while building and testing our delorean
package. As you can see, it installs fine without much issue. The tests run and we have one exception.
00:23
It says that the 'datetime.timedelta' object has no attribute 'total_seconds'
. That is interesting. This likely means that .total_seconds()
was introduced in Python 2.7 and is not available in Python 2.6.
00:38 We can easily solve the issue here, and let’s take a look at it real fast in the source code. So, let’s open up that particular test.
00:50
Let’s see here. It was epoch test that was failing, and it’s simply saying to get the epoch time and assert that. So the method we want to look at is the .epoch()
in the dates
module.
01:03
Excellent. So, this is the portion of the test that is failing, and this apparently isn’t available in Python 2.6. So, we can easily fix this issue, and I believe this is the only place that we use .total_seconds()
. Let’s just take a look here.
01:20
Yeah, this is the only place. So we can easily rework this to make it compatible with 2.6. So all we need to do is write a function to get total seconds that takes a timedelta
and returns
01:35
the number of seconds that it represents. Let’s just quickly just write out a docstring for this, so we know in the future what it does. I tend to forget what methods I wrote yesterday do. So let’s just simply say 'This method takes a timedelta'
'and returns the number of seconds it represents with the resolution of 10 ** 6'
.
02:14
Awesome. So let’s return. Now, we all know that—or hopefully we all know—that timedelta
has a .seconds
, .milliseconds
, and .days
attributes, which they use to represent the time difference between two datetimes.
02:29
So, in order to get the total number of seconds, we have to add those all up together. Let’s first deal with microsecond, so td.microseconds
plus we then have
02:48
td.seconds
plus td.days
times 24
hours in a day, and 3600
seconds in an hour,
03:04
then multiplied by 1e6
, then divided again by 1e6
. And that should provide us with the same functionality that the .total_seconds()
did. So we’ll simply rewrite this so that it takes the delta as an argument, get_total_second()
, and then we’ll pass delta
in.
03:32 Beautiful. Let’s make sure that actually works. Let’s run our tests here,
03:43
We have an issue here, has no attribute 'day'
. Let’s go back here, I must have miswrote something here. .days
is what we actually want. Let’s re-run the test again. Yay.
03:53 Everything works, this is awesome. So, when converting an application from any version of Python or anytime you’re changing code and you have tests, you should rewrite the thing that you’re trying to mimic or improve, and then run the tests to verify that none of the functionality has changed.
04:13
So, before we had a test for test_epoch()
, it would return the correct number of total seconds since January of 1970. We then realized that this wouldn’t work in Python 2.6.
04:24 We wrote a workaround. We used that same test that we had in the previous version to verify that we did not break any of the current functionality in the library.
04:34 This helps with dealing with regressions so you don’t fix something and then break something else. I’m going to run this through Travis and when we’re back I’ll show you how everything worked, and then we’ll move on to fixing Python 3.2.
Become a Member to join the conversation.