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.
Did you think that list comprehensions were a complicated thing? I suspect you’ll think again about complicated things in Python while reading this text.
By definition, lambdas are short functions defined in place, used for small data processing tasks. There are 2 reasons on why they should be used:
Execution speed – they can be optimized by compilers, first by removing an actual function call, next by opening the door for more optimizations through any possible internal (by the compiler) code re-arrangement;
Writing less code.
A Python example of a n square lambda:
g = lambda x: x ** 2 # e.g. g(3) yields a value of 9
Lambdas are usually used in conjunction with data processing functions such as filter, map and reduce.
If we want to select only a portion of some input, the filter function comes to its best use in combination with a lambda:
print filter(lambda x: x % 2 == 0, xrange(0, 11)) ... [0, 2, 4, 6, 8, 10]
Applying a transformation function to the entire input is a job for map:
print map(lambda x: x * 2, xrange(0, 11)) ... [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
Processing the entire input in order to get a single final result is a job for reduce. Please note that 2 arguments are required for the lambda function used for processing; the first 2 elements are used in the beginning, then the lambda result and the next element are used until the input is exhausted.
print reduce(lambda x, y: x + y, xrange(0, 11)) ... 55 # 1 + 2 + ... + 10
Now for some serious stuff: