sifry_london_bar.jpg

Photo courtesy of my boss and friend, Dave Sifry who is in London right now attending MozFest; lucky guy!

In the continuing saga of "Once upon a time I did ruby exclusively, now I do python and ruby side by side and I keep being stupid", I have discovered something subtle and interesting about how python works internally.

Let's start with a function definition in python:

expert_name = "Anti Semitic Speech Expert"
expert_version = 0.01

def make_opinion_template():
    template = {}
    template["name"] = "slurs"
    template["raw"] = None    
    template["score"] = None
    template["opinion"] = None
    template["expert_name"] = expert_name
    template["expert_version"] = expert_version
    return template

The first two lines are globals and all the next line does is construct a hash (ok dict if I have to talk python). I then embedded this method in my service and ran it using Firefly so I could call into it across the network and I got this curious error:

  File "/Users/sjohnson/Sync/fuzzygroup/adl/antihate/antihate_experts/venv/lib/python3.7/site-packages/firefly/app.py", line 170, in __call__
    result = self.function(**kwargs)
  File "/Users/sjohnson/Sync/fuzzygroup/adl/antihate/antihate_experts/expert_antisemitic_speech_service.py", line 107, in opinion
    new_opinion["raw"] = json_results
TypeError: 'function' object does not support item assignment

but new_opinion isn't a function – it is a dict – so what the heck is going on???

Note: If you're an experienced Python wrangler, you already got it – I know, I know – I'm slow as hell at times particularly when it comes to syntax changes.

In ruby you can call methods with or without parens and the issue here is I had this syntax:

opinion_template = make_opinion_template

instead of:

opinion_template = make_opinion_template()

I simply forgot to add the parens when I called make_opinion_template. Sigh. The way I ultimately found this was to use a pdb.set_trace() call and evaluate opinion_template in the debugger.

> /Users/sjohnson/Sync/fuzzygroup/adl/antihate/antihate_experts/expert_antisemitic_speech_service.py(109)opinion()
-> new_opinion["raw"] = json_results
(Pdb) new_opinion
<function make_opinion_template at 0x13e328ef0>
(Pdb) quit()

and then, after the fix:

> /Users/sjohnson/Sync/fuzzygroup/adl/antihate/antihate_experts/expert_antisemitic_speech_service.py(109)opinion()
-> new_opinion["raw"] = json_results
(Pdb) new_opinion
{'name': 'slurs', 'raw': None, 'score': None, 'opinion': None, 'expert_name': 'Anti Semitic Speech Expert', 'expert_version': 0.01}

Personally I would have thought this would have been syntactically invalid but when a compile check didn't find an error:

python -m py_compile expert_antisemitic_speech_service.py

I am forced to admit that this must be lexically correct for some reason that I'm too unskilled at python to understand.

So, a short note to myself: Don't forget the parens when you call methods.