 Good morning. Good to see already so many people so early in the morning. I have a presentation about the solid principles applied to Python. It's a presentation that's going to be a bit more technical, a bit more theoretical. It's not an easy topic. It's more about the fact that quite often we have software that's quite complex, that is difficult to maintain, unstructured. And the solid principles are actually tools or principles to make sure that your software is structured. This is me, Jonathan. I'm working for Cisco right now. You can find me on GitHub or Twitter. The solid principles are actually the first five principles named by Robert Martin. And he's also known as Uncle Bob. And these are the five principles. So first we have the single responsibility principle. And that's the idea that a function or a clause or a module is supposed to do only one thing. Or as Robert Martin puts it, a clause should have only one reason to change. If you think of a software as a complex problem and you want to write it in code, the first thing that you do is try to decompose that problem in many smaller sub-problems. And in each of those sub-problems probably you're going to solve in a separate function. So if you keep decomposing things and smaller and smaller things, then you end up in a situation where all those functions are doing just one thing. And probably that's where you have most of the reusability. So in that case, every single function will do just one thing. So that's the first principle. The second one is the open-close principle. And that means that when you made something like a function or a clause, it should be open for extension but closed for modification. That means if you think about it like a function or a clause that does just one thing, it should be possible, for instance, to change the behavior by building around that. So if you want to extend it or if somebody wants to add functionality, he should not have to modify existing code. But the APIs and the interfaces between all those components have to be chosen in a way that you can build around things and change the behavior of existing code. Actually as much as possible with rotary writing code that exists. I think that also applies to when we are building libraries in software. If we have an open-source project at some point, you define the scope of your library. And if you do it the right way, then you should expose an API that people can extend it as much as possible without having to contribute to the library itself. So if the library does just one thing and the scope is very well defined, then at some point it should be possible to consider the library done and that there's only maintenance to be done. And every functionality that has to be added should be possible to build around the library. Then third, we have the LISCOP Substitution Principle. I'm going to skip it for now and the Interface Segregation Principle as well. They're very interesting, they're very important. So if you want to know more about those, there's a lot of articles about that. We don't really have time for those two. Then finally we have the Dependency Inversion Principle and that's the principle that I intend to cover in this presentation. And actually it all goes, it's all about bad code. So we all write bad code and we write code that's often unmaintainable and so on. And the reason for that is, as Martin says, that we don't have a lot of time. We don't have time to do things the right way and that's why we write bad code. But the consequence of that is that actually it's having that bad code what Slows is done. Because making changes to bad code is difficult. Because sometimes maybe you want to try to change one thing in one place and something else in a completely unrelated place starts to break. So that's a bit weird that we write bad code because we don't have much time but at the same time it's having bad code that Slows is done after some time. And then code starts breaking at some point. So the Slows principles are actually principles that give you some discipline and write tools for avoiding that kind of bad code. The reason why bad code often breaks is that there's something called coupling in code. That means that one piece of code is very strong connected to completely different piece of code. And so having a good code architecture actually comes down to managing the amount of coupling that we have between pieces of code. One way, of course, an easy way is to avoid global variables. Everyone knows global variables. That's a very easy tool to transfer information from one place in a program to a completely different place. Global variables allow you to just connect to random pieces of your program together and transfer information between those pieces. But of course, global variables, they introduce very strong coupling and they only lead to unmaintainable code. This is a call graph. So every program has this. You see at the top the main function, that's where the program starts. Then a few other functions underneath that which are called by the main function and then at the lowest level, yet other functions that are called by the middle layer of functions. The arrows are pointing in the direction from which function calls the other function. So now we are going to look at one of those arrows. We see that the function f calls the function g. So that means at runtime, there is some flow from f to g. But if we would look at the source code, we also see that in order for the function f to call the function g, there need to be some kind of reference. The function f needs probably to have some import of the function g, otherwise you cannot call it. Or maybe it's in the same file, but at least in the source code there is a reference from the function f to the function g in order to call it. And that means actually that we have two arrows pointing in the same direction. There's runtime dependency from left to right, but also a source code dependency from left to right. If we look back at this call graph, we have high level code, mid level code and low level code. And with all those arrows in between, then we can actually see that the high level code calls the mid level code. So there is the flow, the runtime flow from high level to mid level code, but also in the source code. The high level code refers to the mid level code in your source, and the mid level code refers to the low level code. So actually that means that high level policies depend on low level details. And if there's one detail that changes, like if one of the functions at the lower level would change the interface, the high level code has to adapt. It also means that let's say we want to reuse the function f3. We want to pull it out of this application in order to use it in a different application. We are unable to do that without pulling the green one along, because f3 depends on f11 in this case. So actually we want to find a way to reuse the f3, the red one, and maybe putting something else underneath. So you want to be able to pull the logic of f3 out of this application and put it in a completely different context. That's what the inversion principle tries to solve. I found this picture. Would you soldier a lamp directly to the electrical wiring in a wall? We're not going to do that, of course. If we want to connect the lamp to the electricity, we think of an interface, and probably that's something like this kind of connector. And this allows us to replace the lamp, for instance, by a different lamp. Or if we want to connect the lamp to a different power supply, like a battery, we can replace the power supply as well. So we think of an interface between those two components. So for the dependency inversion principle, we go first back to Python code. And there's something that we need to understand first, which is called polymorphism. So let's say that we have a library that does some HTTP parsing. The function on the right side is an HTTP parser. And if you think about an HTTP parser, usually it works by taking some text as input, parsing it, and then doing something with the result, and probably sending something back over the network. And typically an HTTP parser works by having some network connection underneath. So you receive some data from the network, you parse it, and you send some data back to the network. But actually there's no reason for the HTTP parser to not parse the data from a file. So let's say you have a dump of your HTTP connection in a file, maybe for logging purposes, and you want to parse the same information from a file. So that should also be possible. And we abstracted that in this case by having a transport object. So on the left side we have two transport objects, a socket transport and a file transport, and both of them implement a method getData. And then on the right side our HTTP parser takes a transport object as input, and it calls the method getData of that transport object. So that means for the HTTP parser that it does not have to care about which underlying transport it is. So in the yellow box we see how that we can use it. We create a socket transport, it passes to the HTTP parser, and the HTTP parser uses that. Actually the HTTP parser in this case is the high level abstraction. The high level abstraction that does not care about the underlying details like the IO transport. And the two implementations on the left side are the IO transport. So this is polymorphism. The HTTP parser calls the getData function from any of those on the left depending on which object we pass in. And notice that actually we don't need inheritance for having polymorphism. I'm going to skip this slide. Now we are going to try to formalize the interface that we defined between the HTTP parser and the transport layer. And we can do that in Python by using abstract-based classes. So there's a built-in module called ABC. It ships with the Python distribution, so we don't have to install it. And ABC, it stands for abstract-based class, and it allows you to formalize the interface. The way for doing that is creating a class, in our case transport or IO transport, and you pass the metaclass ABC meta. It uses metaclasses underneath because it changes in a way slightly the behavior of the class, but that's not super important how it works. And then every method that we expect for objects implementing this interface to have, we have to decorate them with abstract methods. So the ABC abstract method decorator says that there should be methods called getData that has to be present. Similar to abstract method, there is also abstract property. So let's say that you want your objects to have a certain property. You can add abstract property as a decorator. And a nice thing about this is that the abstract-based classes in Python enforce you that objects of this type have those methods present. So using that, so at the top we have the interface. On the bottom we have the implementation for the interface, so this is actually the concrete class. So we have a socket transport that implements the transport interface, and so it provides a function getData. So the abstract-based classes, as I've said, they prevent you from creating an instance of a class if you forgot to implement a certain method. So that means if we had a socket transport, but we forgot to implement getData, then Python will disallow you from creating an instance of that transport class. So actually we are sure that whenever we have an instance of something which is a transport, that we can be sure that all the methods that we expect are actually present. So going back to polymorphism, now it will look like this. So both implementations of the transport layers on the left side inherit from transport, and on the right side we can actually add an assertion. We can say assert is instance, making sure that our transport parameter is an instance of that transport class or transport interface. If you use Python, I guess 3.5, you can also use type annotations, which are really nice. And then probably if you have a type checker name, it will check whether the thing you're passing in actually implements that interface. And the rest remains the same as before for the polymorphism. Another way for telling Python that a certain class actually implements a given interface is by using the register function. I forgot to rename something. So input device should have been transport, and then we could have done transport.register, some other transport, and that's actually a way of telling that some other transport also implements that interface. So rather than using inheritance for telling Python that something implements an interface, you can use the register function as well. So we have on the left our HTTP parser, and it calls on the right side our getData function, socket.getData from the transport. But from the point of view of the HTTP parser, it's not calling that concrete class socket, but it's calling transport.getData. So from the point of view of the HTTP parser, we program against an interface, not against a certain implementation. And the interface is actually the transport abstract-based class. And now if you think about it, who is responsible for defining that interface? Is it the HTTP parser, or is it the implementation of our transport? And in fact, it is the high-level code that should define the interface that the low-level code should use. So the HTTP parser is going to define the interface for the transport class. And then we end up in a situation like this. So we have on the left side our high-level code, the HTTP parser, with the interface that it expects the underlying transport to implement. And then on the right side we have a transport. And you see that the arrows actually point now in a different direction. So at runtime the HTTP parser calls the getData function from the socket. But if you would look at the source code, actually we have the orange arrows. The low-level socket code refers to the transport of the high-level code. So actually it's the low-level code that adapts to the abstractions that the high-level code is expecting. And the arrows that we flipped over here is what we call the dependency inversion principle. So here we solved that connector problem, actually. Now if we go back to our call graph, we have everywhere high-level code that calls low-level code underneath. We should try to draw lines in there and try to think about interfaces between high-level abstractions and low-level details. And that is what should allow us to replace the functions that we have at the bottom. So let's say we want to reuse F3 out of these applications, but we want to put something else underneath. If we had to design an interface between F3 and F11, then we should have been able to replace F11 and reuse that in a different application. And the same for unit testing. The idea of unit testing is actually that we should be able to test one function out of the context. So looking at this call graph, if we want to unit test this, we want to unit test every function individually out of the context. So we want to be able to pull one function out of the call graph, provide it with some input, and check, verify whether the output matches what we expect. If all the high-level abstractions depend on those low-level details, without proper interfaces in between, then it would be impossible to test, to unit test the high-level code without having the unit test actually call the code underneath. And that's actually a problem. You don't, for instance, you don't want your unit tests to connect to a database. Well, sometimes you have to, but in general, you want to avoid it because it makes running unit tests extremely slow. You also don't want unit tests to read and write to files, for instance, or not too much, but at least not to the network. So actually, for unit tests, you want to avoid that they do IO as much as possible. And what we typically do in unit testing is creating a mock object, like the unit test mock object. And we are replacing the IO layers in our application before a unit test. And a common way of doing that is by monkey-patching. So monkey-patching is the practice of, at runtime, we're placing objects by different objects, and then the code when it executes, it calls your mock object rather than the actual original object. However, if we defined proper interfaces between high-level code and low-level code, the low-level code is mostly the IO layer, then rather than having to monkey-patch, we can create that mock object and adjust the mock object in a way that actually implements the interface that the high-level code expects. So we just registered that mock object as if it would implement that interface, and we pass it to the high-level code. So using inversion of control, actually there's no reason to monkey-patch anymore, and that's quite nice. So that allows us to test each function individually, or each component at least. You don't need to have an interface between every function call that would be a bit too overkill, but at least between some reusable components and between high-level code and low-level code. It's good to have interfaces in between there. I have a slide about async.io, because since async.io was created, we actually are in a situation with Python where we have some duplication in our libraries. For every possible protocol that we have, in Python we have the original ones on the left, the synchronous protocol implementations, and then on the right we have those asynchronous implementations. So actually when async.io was created, everyone started creating new libraries for handling those protocols in async.io, and it really felt like we were doing a lot of duplicate work. And actually that was one of the critics against async.io that it's impossible to reuse the standard libraries in an async.io application, because they were doing blocking calls, which we don't want. So what's the problem, actually? In a way we failed to decouple the high-level code from the underlying abstraction, from the underlying I.O. layers, actually, because the only difference between synchronous code and asynchronous code is that the I.O. is slightly different. The I.O. layer is something else. So rather than re-implementing every protocol again in async.io for using it over there, we should have been able to decouple the I.O. from the high-level abstractions, like the libraries, the parsers that parse HTTP, Postgres, Redis, and so on. Of course, in reality, this was not obvious. In fact, I made async.io Redis myself, so I made the same mistake at some point. In reality, it's not really obvious to always decouple that code, but I guess if from now on we ever have to implement a new protocol, like something that does not, something for which we don't have a synchronous version yet, then it could make sense to design our application in a way that we are able to decouple the high-level parser logic, which is completely CPU-bound, and the underlying I.O. transport, and then define that, yeah, couple that in a way that we can have both a synchronous and an asynchronous version using the same code ways. That could save us a lot of time in the future. I guess I'm going to skip this class, and I've said that. So to conclude for this talk, the solid principles are actually all about decoupling code. It's all about being able to draw boundaries in your application, and have reusable components. So I'm going to draw boundaries in our application between those components with proper interfaces, then it's easy to decouple those components and reuse them in different applications. And the inversion of control principle is about writing a high-level code that depends on abstract interfaces. It doesn't depend on a concrete interface, but it depends on the abstraction, and that way we're able to swap the underlying implementations. And the nice tool for doing that is using Python abstract-based classes. So it's really nothing more than importing ABC and defining the interface. And that's all what I have. So thank you. Yes. So the question is whether I have an example about unit testing for using abstract-based classes. I don't have something that I can show right now, no. Any other questions? No? Okay. Thank you.