In my previous post I touched on the basics of how you can use pytest to test your code. In this post I’ll be covering how you can use Allure2 to prettify your pytest results.
Allure2 Adapter for pytest
The first thing we need to is install the Allure adapter for Pytest. As the documentation states, this repository contains a plugin for py.test which automatically prepares input data used to generate Allure Report.
Issue the following command to install the adapter:
sudo pip install allure-pytest
If you’re fairly new to coding chances are you’ve run into an issue where you make a minor change in one place, and then end up breaking your script in another place. In order to find out what went wrong you start adding
print statements all over the place to debug your code.
While it sound like a good idea, what you’re actually doing is relying on Python to tell you when you’ve made a syntactical error. However, what if your syntax is find, but your code is incorrect?
For example, say you accidentally changed your
addition function to a multiplication function by replacing the
+ with a
def addition(a, b):
return a * b
In my previous post, Python: Scope, I touched on the topic of Shadowing. In this post I’ll be delving deeper into it.
As Wikipedia says, variable shadowing occurs when a variable declared within a certain scope (decision block, method, or inner class) has the same name as a variable declared in an outer scope.
There are some interesting debates on whether shadowing is a bad thing or not in this StackOverflow Q&A as well as this one. In a nutshell, there are three trains of thought:
- It’s fine to use shadowing.
- You should avoid shadowing by ensuring all names are unique.
- You should avoid shadowing by using functions.
Let’s now run through each of these options to see how they work.
Scope is the term used to define the location(s) in which Python searches for a name to object mapping (e.g a variable).
As described in this StackOverflow post, Python uses the LEGB Rule to locate a definition. LEGB stands for:
- L, Local — Names assigned in any way within a function (
lambda)), and not declared global in that function.
- E, Enclosing-function locals — Name in the local scope of any and all statically enclosing functions (
lambda), from inner to outer.
- G, Global (module) — Names assigned at the top-level of a module file, or by executing a
global statement in a
def within the file.
- B, Built-in (Python) — Names preassigned in the built-in names module
In a nutshell, Python will first look at the local scope for a name to object mapping (e.g
people = 5). If it cannot find one, it will continue going up the hierarchy until it finds one. If it doesn’t find a mapping, it will raise an exception.
To shed some more light on this let’s take a step back and analyse each of the points listed above. I’ll do so in reverse order because that is the way we write Python code, as you’ll see in a moment.
A lot of Python books often mention that “everything in Python is an object”, and “objects are first class citizens”, but they don’t always explain what that these things actually mean. Let’s try to fix that up now.
Everything in Python is an Object
Dive Into Python gives a great explanation:
Different programming languages define “object” in different ways. In some, it means that all objects must have attributes and methods; in others, it means that all objects are subclassable. In Python, the definition is looser; some objects have neither attributes nor methods (more on this in Chapter 3), and not all objects are subclassable (more on this in Chapter 5). But everything is an object in the sense that it can be assigned to a variable or passed as an argument to a function (more in this in Chapter 4).
This is so important that I’m going to repeat it in case you missed it the first few times: everything in Python is an object. Strings are objects. Lists are objects. Functions are objects. Even modules are objects.
There are plenty of articles on the internet that attempt to explain what
if __name__ == "__main__" is and what it does, but (in my humble opinion), the examples are too complex more often than not. With that in mind, this post is aimed at being the most simplest explanation on the planet! :)
What does it do?
This statement is used when you want your code to be used as both a standalone script, as well as a module that can be imported and used by other scripts.
For example, if it is run as a standalone script, you may want to provide a menu to ensure users input all of the necessary information. On the other hand, if it is being imported as a module, perhaps you’d like to avoid the menu all together and instead only use the functions contained the script (e.g
func1 in the example below).
In my previous post I discussed what a compiler does. In this post I will cover Interpreted code, Bytecode and Just-in-Time compilation. One thing to note at this point is that while a compiler is able to create a standalone executable application (e.g an .exe file in Windows) which does not depend on any other application in order to run, the same is not true for the three methods.
If you’ve used Linux before and/or have coded using a language such as C, you will have come across a compiler. Though because they’re so easy to use, you might not have actually understood what it is they do.
In a nutshell, compilers simply (I say simply because what they do is actually very complex under the hood) check for syntax correctness, convert source code from “high level” languages to a low level (native) language which your CPU understands and perform optimisation among other things. Due to the conversion and optimisation of code, compiled applications run much faster than when other methods are used. The result of a compilation is a standalone executable application (e.g an .exe file in Windows) which does not depend on any other application in order to run.
You’ve probably already heard that Python is a strong, dynamically typed language. But what does that actually mean? Let’s jump in and take a look.
Dynamically vs Statically Typed Languages
This StackOverflow Q&A gives us an idea on the difference between Statically and Dynamically Typed languages:
A language is statically typed if the type of a variable is known at compile time. For some languages this means that you as the programmer must specify what type each variable is (e.g.: Java, C, C++)
In Learning Python (a must read for anyone interested in Python!), author Mark Lutz refers to statements and expressions frequently, but give a clear explanation of the two.
Rather than reinventing the wheel by providing my own definition and examples of the two, I found this Quora Q&A does that perfectly:
A statement is a complete line of code that performs some action, while an expression is any section of the code that evaluates to a value. Expressions can be combined “horizontally” into larger expressions using operators, while statements can only be combined “vertically” by writing one after another, or with block constructs. Every expression can be used as a statement (whose effect is to evaluate the expression and ignore the resulting value), but most statements cannot be used as expressions. Here’s a complete list of statements.