GSoC Update - Week 1 🚀
Before The Coding Period (For context)
My exams were to start on 7th June. Knowing that, I started working a week or so before. Mind you, I am fairly new to Django - with no experience in Web Development. I knew that this was going to be a hurdle for me, so I started a little early to compensate for my lack of experience and the time required for my exams.
I created custom Django TestCase classes so that I could easily test views, then wrote some tests, learned some Vim shortcuts.
I had some trouble initially when I was trying to test a view that created comments on the Inkscape Forum [1] using Django's Test Client. To me, it should have been a simple matter of passing a POST request to a URL. Turns out, django_comments, the package the website uses for comments, does some special things under the hood for security reasons (which I won't go into too much detail). It attaches some security information to the form and sets that info as type=hidden in the HTML so that the browser sends it back to the backend. It works in the browser because the browser first sends a GET request on the URL, which renders an empty form. The form has those hidden inputs. It has the complete state essentially for the browser to send a complete valid POST request.
Since I was using the Django Test Client, I could only send a POST or a GET request to that URL ( I could theoretically send a GET request first, then parse the HTML to extract those fields, and then send the POST request... ) but no, I was adamant in doing it the browser way. I blatantly suggested to my mentor, Martin that we should use Selenium for these tests...
He politely asked me to send that extra information in the POST data itself instead of adding Selenium as a dependency. Little do you know when you want to add yet another dependency for no prominent reason. You can go as deep as you want in that rabbit hole 😉 [2] (Why not add numpy while we're at it?)
Now, the problem was - how to retrieve that security data?
Anyways, this was before the coding period.
The Coding Period Begins
Mocking and Patching
I also looked into Mocking and Patching this week. Basically, mocks are blank stubs that you can substitute for real objects while testing. They are an extremely handy tool. You can mock almost anything (almost... more on this later) - databases, external APIs, UFOs 😉... Mocks are part of the Python standard library, present in the unittest testing framework. So, mocks are stubs and you replace real objects with them using patching (also a part of unittest).
The need was to mock and patch datetime.now() for a test. This test was going through all the topics in a forum and filtering the topics which were created in last n number of days. Since test data is static, the tests run on data you created a month ago will show different results when you run datetime.now(), well, now. You might get a false sense of test correctness when you run them now but after a month, a year, those tests will suddenly start to fail. I hope I was able to convince you that the need for mocking dates in tests is real.
Turns out it is hard, real hard to mock datettime.now(), especially when using third party code like Django. I won't go into the details of why it is so, there are some great articles about that. I'll link to them in the footnotes.
With my novice Python skills, I came up with a solution of my own:
from contextlib import contextmanager @contextmanager def freeze(datetime_module, set_to): patcher = mock.patch.object( datetime_module, 'datetime', mock.Mock(wraps=datetime_module.datetime) ) mocked_date = patcher.start() mocked_date.now.return_value = set_to yield patcher.stop()
Then you would use it as:
import datetime def test_view_displays_topics_from_last_week(): with freeze(datetime, set_to=datetime.datetime(2021, 6, 8): # do actual work pass
isinstance(value, datettime.datetime)
The problem is that the only way that the datetime.now() method can be patched is if we mock the parent datetime class. That is datetime.datetime. In doing so, we essentially replace datetime.datetime with an instance of Mock class.
So, now roughly
datetime.datetime == Mock()
Those isinstance checks will now fail when the value argument is a real datetime.datetime object. Since it would be asking if a date is an instance of a Mock instance (yes, an instance, not even a class!).
So, what's the solution then?
GSoC Mentors and Mentees Meet (G3M)
We had our first G3M (I gave it that name, it's not official) on 11th June. All the mentors introduced themselves and told us about their backgrounds and what part of Inkscape they work on. Then students told about what they're doing currently, which university/school they're from and what project they will be doing for GSoC (We also have one project from Outreachy). There are many interesting projects this year - on canvas bool ops, markers, alignment guides, actions improvements, etc.
In the meeting, Marc, one of the developers, suggested us to write blogs, and communicate regularly with our mentors about our work. This G3M was scheduled on a BBB instance. This is how all the developers usually communicate. That, the RocketChat instance and IRC are the modes at our disposal. We were also told that we will be having more such G3Ms in the future.
This was a fun experience. I got to know about the people who develop Inkscape. Together with the community they create an awesome piece of software that is Inkscape. I am glad I chose Inkscape as my org.
Final Thoughts
This week was a light one with respect to how much I could do. I learned many new things and got to know such great people. I hope I will be able to contribute more in the coming weeks.
Bonus Tip: Just Grep It!
If you are trying to find correlations between how a thing is done in the codebase, an intuitive way is to search for it. Should you use your IDE's search? Nah... I found VSCodium's search to be ineffective sometimes so I have given up on it. Instead, I use this:
grep -Rin "?next" .
Example output for this search.
./content/second.rst:121: grep -Rin "?next" . ./content/second.rst:132: grep -i "?next" $(find . -print) 2>/dev/null ./output/2021/06/the-beginnings.html:244:grep -Rin <span class="s2">"?next"</span> . ./output/2021/06/the-beginnings.html:255:grep -i <span class="s2">"?next"</span> <span class="k">$(</span>find . -print<span class="k">)</span> <span class="m">2</span>>/dev/null
Before I knew this command, I used to use this:
grep -i "?next" $(find . -print) 2>/dev/null
Anyways, that's all I have to say for now, adieu...
Too many mountains to climbToo many thoughts for my pen to write...- Too Many, Winterbourne
Footnotes
[1] | Inkscape Forum: If you haven't checked it out, have a look, there's lots of cool things the community has shared. |
[2] | I have nothing against Selenium, I have everything against my tendency to complicate things when things can be simpler. Selenium is an excellent tool, I was going for the chainsaw instead of a knife. |
[3] | https://nedbatchelder.com/blog/201209/mocking_datetimetoday.html - Ned Batchelder talks about his need to mock today(), similar to my need to mock now(). |
[4] | http://lists.idyll.org/pipermail/testing-in-python/2011-July/004296.html A conversation with Michael Foord - the original author of mock, that highlights exactly the problem I was having. |
[5] | https://youtu.be/FEQstRH73WI - A great learning resource. Teaches how to navigate/search a big codebase such as Inkscape. The thought process is the most important takeaway from this video. |