bugfix_wrapup.txt
1
Hi, my name is Calvin Shin, and I am a former FB Open Academy student who was 
2
assigned with fixing a bug, and I would like to explain how I fixed this bug. 
3
Let's get to it!
4
5
The initial issue at hand was that as an admin of Review Board, you could not 
6
add a private Github or Kiln repository. It displayed a message that read 
7
" 'URLError' object has no attribute 'read()'." Once I tackled this, I 
8
changed the error message so that it could properly convey why the error was occurring.
9
10
Now, for a step-by-step process on how I tackled this HostingService bug-fix. 
11
First, the error message that popped up when I reproduced this bug was a good 
12
indicator of where I should look in the code to start. Keep in mind the scale 
13
of the Review Board codebase in comparison to that of your class assignments 
14
and projects. So the error message really saved me a lot of time. Debugging 
15
can really get you lost for hours with a codebase like Review Board. 
16
Anyways, proceeding with this "hint," I did a quick find in Pycharm, and 
17
along with the help of a mentor at my school's Code Sprint, I found the 
18
section of the code where it could have been producing that message to 
19
appear. This lead me to a part of the code in github.py, into a function 
20
called 'authorize'. I looked around in this function and saw the line 
21
"data = e.read()". At this point, I saw that the line was in a clause to 
22
handle a HTTPError and a URLError and thought that one possibility (deducing 
23
from the error message) could be that e was a URLError and that the line 
24
"data = e.read()" was causing this message because a URLError object doesn't 
25
have the ability to call the read() function. Confirming this with a mentor, 
26
I fixed this by making a simple change. I put the exact same line inside a 
27
try block that was previously right underneath it. My reasoning for this was 
28
that it was failing because data contains an invalid statement (URLError 
29
doesn't have attribute read) and so putting it in the try block would attempt 
30
to execute that line, but if it didn't work, it would just go ahead to the 
31
except block, which was right after the try block. My mentor said to make 
32
the same changes where there were other areas of the code that had similar 
33
lines, and so I made the same changes in another function in github.py called 
34
'_check_api_error', and a function in kiln.py called 
35
'_check_api_error_from_exception'. I tried reproducing the bug with the same 
36
steps to see if I had fixed the bug, but this time I encountered another 
37
error. This one gave another error message reading 'URLError object has no 
38
attribute code'. This time, I traced the code back to '_check_api_error', 
39
because that was the only place out of the fixes in github.py that I made 
40
that contained any text of "code." I saw that the variable e, which was 
41
assigned URLError, was calling something that it couldn't; this time, 
42
something called code. Only HTTPError, a subclass of URLError, has attribute 
43
"code," so I fixed this problem by handling these two errors separately 
44
(because both HTTPError and URLError could be assigned as e). If you take a 
45
look at '_check_api_error' now, you can see that HTTPError and URLError are 
46
handled separately. This is the last set of if-else statements in the 
47
function. I changed it so that the HostingServiceError, which previously had 
48
the variable http_code=e.code as its second parameter, does not have a second 
49
parameter and instead passes in the default parameter set for http_code, 
50
which is assigned None. This finally allowed Review Board to display the 
51
correct reason for the inability to add a Github repository, but this message 
52
showed "<urlopen error [Errno 61] Connection refused>". Because this error 
53
message has no significance whatsoever to a user of ReviewBoard, I changed 
54
this to read "Could not connect to the server "<domain name>". If the server 
55
is behind a proxy, make sure the proxy settings are correct." This message 
56
essentially says tells the user that the reason that he or she cannot add a 
57
repository is because Python isn't properly configured to a proxy server.
58
59
Cool, so it seems like the bug is fixed, but this is only the halfway mark in 
60
a sense. Now that we have what we think is working, we need to verify with 
61
unit tests. Unit tests ensure that whatever behavior we are expecting works 
62
properly and accordingly and does not have any changes. This was the part I 
63
had the most complications with, mainly because I was required to trace a lot 
64
of code and "deep-dive" into code I wasn't working with or familiar with. On 
65
top of all this, I only had minimal experience writing my own unit tests for 
66
working code. 
67
68
69
Let's take a quick detour to give a sense of how to trace code. In Pycharm, 
70
you can use a shortcut by pressing on the cmd (Macs only) key and clicking on 
71
a variable or a function or a class. This will directly lead you to that, in 
72
whichever file it is in. For instance, let's take a look at a function called 
73
'get_file_exists', in kiln.py, which is an example of an API call (explained 
74
in the next paragraph). Starting with this function, you can see that it 
75
returns True, if it successfully calls another function called 'get_file'. 
76
Using the keyboard shortcut will directly go to the function in whichever 
77
file it is contained in. This shortcut leads you to the 'get_file' function, 
78
and observing it, you can see that it calls another function called 
79
'get_raw_file', which in turn calls 'api_get' on a url. Finally, this calls 
80
'http_get' on that url, which performs a GET request on that url. This is 
81
assigned to a variable called data and returned. 
82
83
Going back to my own unit test, I started off with writing an individual unit 
84
test for each individual fix. So for instance, I made two fixes in github.py 
85
and one fix in kiln.py, even though they were contributing to just one bug. 
86
I wrote 3 individual unit tests, two to test using a Github repo and another 
87
one to test using a Kiln repo. I will explain my writing of the unit tests 
88
using the github unit test that tests '_check_api_error'. I first started 
89
off with writing the unit test function signature, which is simply 
90
'def test_check_url_error (self)'. All unit tests in Python begin with test 
91
and do not have other parameters. Secondly, I used the concept of a "spy." 
92
A spy essentially intercepts and records calls to functions (more about 
93
function spies here: https://github.com/beanbaginc/kgb). In this case, I 
94
"spy on" the http_get function and faked the original function so that it 
95
will raise a URLError, for the purposes of testing our code. After this, I 
96
retrieve the proper hosting service and finally call assertRaisesMessage, 
97
passing in the error message that I fixed, so that the test can extract a 
98
known result. In the call to assertRaisesMessage, I make an API call by 
99
calling '_test_check_repository' (which calls '_check_api_error'), so that 
100
the unit test can actually test something that the HostingService would do. 
101
In this case, it is to check a repository; this function makes a proper API 
102
call because it reaches 'api_get', which calls 'http_get', the function that 
103
is spied on to raise a URLError. 
104
105
106
Ultimately, this bug that I thought I had fixed at the code sprint, or at 
107
least by the end of my first week, resulted in me working on this as 
108
essentially another project, along with the project I had chosen to work on 
109
throughout the term. I honestly was quite annoyed when everybody else 
110
finished their bug fixes their first week and spent the majority of their 
111
time on their individual projects. However, from this one bug, I learned how 
112
to trace and follow code, from one function to another, in a huge project 
113
with a lot of files and lines of code, an invaluable skill for any developer. 
114
I learned how to write unit tests, also an essential skill in industry. 
115
Finally, I gained insight into debugging, which is, in and of itself, another 
116
aspect of software engineering. Thank you for reading this, and I hope you 
117
are now equipped to debug your bugs!
Loading...