As the world evolves, one may want to take on new opportunities to get better things out of the door, e.g. use the “new” async paradigm or make better use of generators and iterators. This means using Python 3 – and this also usually means migrating large codebases from Python 2. Sometimes this is as easy as flipping the bit, other times it may be better to just refactor the whole code. Let me point out some differences, though.
This one is easy – most of the time it’s just about translating code from:
This text is about automating the interaction with Atlassian Confluence, a commercial product widely used in the corporate world for publishing documentation or other content that needs sharing with a certain team, department or throughout the whole organization. This product is usually integrated with the other Atlassian tools such as Bamboo or JIRA.
By “automating the interaction” I understand document publishing; one may extract data from other components of the system using automated tools and may want this data published in a nice format (if possible). This is indeed possible through the REST interface that Atlassian provides for all its products. The REST functionality does not cover all the features or functions, though, but it’s enough for our scenario. The reference for the Confluence Cloud version can be found here. Please note that the interface may be a bit different between versions, so please check the API reference for your particular Confluence version.
Now that we have the tools and the method, let’s get to the implementation – and obviously, to some code.
1. The default initializer gotcha
Suppose you are in an interview setting and you are being handed a piece of paper with the following Python code written on it:
No explanations, no nothing, just the obvious question: what is wrong with the code above?
… nothing in particular. Yes, nothing is wrong with the code above, it is legal Python code (apart from missing the ending “:” and that no function body is given). Who am I to judge the particular use case – after all, a bug is defined as code that does not run the way the programmer expected it to.
But suppose we add more code to that function definition:
def foo(bar=): bar.append(1) return bar
Now things are getting messier. What would a “normal” Python programmer expect to be the result of calling foo() with no arguments for 3 times in a row?
foo() #  foo() #  foo() # 
Nope. The real result is more similar to:
foo() #  foo() # [1, 1] foo() # [1, 1, 1]
How is this even possible? The issue is with how default initializers for function arguments are handled in Python and this is completely different from C/C++. The scope of the default initializer is not the function body but rather the global scope (something similar to the static variables in C/C++). And static variables are good for caching and keeping state. But yes, I believe we had enough of this.