 Are you ready to do your screen share in a few seconds? Yes, of course. Right on your side. You're calling in from Poland, right? Yes, I do. And so your talk is about type safety. And in the descriptions, you said it goes beyond PEP 48 for the type hints. So it will be really interesting to see what you want to do beyond that path. So if you're ready, please start. OK, thank you. So what I would like to talk about, let's first define what I mean by type safety and also what I mean by full stack type safety. So the type safety that we want to achieve is catching all the errors, all the bugs that are caused by type mismatches as soon as possible. So ideally it would be during the code development, but never later than in CI. They shouldn't cause any bugs that happen later on the QA phase, because this is unnecessary time lost in our development cycle when they have to be found in QA and returned to developers. We want the automatization here. And also we want to catch typing errors that span the layers of stack because we might have errors that are local to some code base. But we also have data that is passed from a layer to a layer and we have contracts between these layers. And also we want some automatic way to find that we are somehow violating these contracts. What are our main problems here? First thing, the system of type annotations in Python is quite new. It is quite immature. Not everybody is using it. It is still in development. So it is not yet perfect, of course. The second thing is we have various paradigms of typing in various layers. And this is also a problem how we can propagate the information about the type from one layer to another. And also the problem is that when we test, we usually test layers in separation. It is much easier to write unit tests than integration test. And it is much cheaper to run unit tests than integration tests. So we mostly write unit tests, not focusing that much on the contracts between various components of our stack. The one thing I would like to do in the beginning is to somehow discuss the terminology because this terminology is used quite loosely in various publications. And sometimes people understand different things by these terms. So I would like to just ensure that we are on the same page so that we understand the idea of weak versus strong in these terms. The typing is weak if the value can be somehow misinterpreted unless we care about it ourselves. And it is strong if a type system is protecting us from misinterpretations. The basic example would be in C. Here we are setting two sign integers to different values. The first one we are putting to print f. But we are using incorrect formatting clause. And we get a value that is completely different and doesn't mean anything for our user. Here we are passing the value by a pointer. We then cast the pointer to a pointer to a different type. And it all nicely compiles. But we also get a value that doesn't make any sense for us because this value that is ordinary int gets interpreted as a short int, actually some bytes of this value. We may think that this is something that wouldn't happen in higher level languages. But I will show how this can happen. Very similar thing can happen in the SQL. So this is not completely relevant for us. Then we have static versus dynamic, which I believe all of the Pythonistas are familiar with. Of course, Python is a dynamic language because the types are determined during runtime, not during compile time. So if we have a Python function without any annotations, we basically don't know what are the types of these arguments. We can pass various very different types here. And they would all work. And even if we put something that doesn't work, we wouldn't know about this before we actually run this. So this is the thing about dynamic typing. And an example of static typing is in Golang, where we can actually always determine what type any variable has, either because here it is marked or because it is assigned to some value of a known time to a literal or to a result of some function. And we know the result of the function here. So static typing doesn't mean that everything is marked, but it means that everything we can determine just by looking at the code and then our compiler can determine it. We have also another distinction that is sometimes called weak strong. But I would like to say the strict versus loose. In strict typing, we have explicit type conversions. We have to cast the values explicitly. If we don't, we get exceptions or compilation errors that tell us that we have a type mismatch. In loose typing, there can be some implicit type conversion. And this thing is actually not a binary thing. This is a spectrum. And to show you, I can show you a language that is stricter than Python. This is Haskell. And in Haskell, trying to do something like this, trying to print an integer would cause error. Trying to pass a list as a sole argument to if. It is also an error. So these are things that we are familiar with from Python. And we can't bring it there because Haskell requires some more discipline for us. On the other hand, there are languages that are looser than Python. Famously, it's JavaScript. In JavaScript, almost every expression we create would be interpreted somehow by the interpreter. We can see, for example, that here, the number is cast to a string. Here, the object is cast to a number and so on. Somehow, this leads to strange things happening. For example, here, we get not a number, but we are not working with numbers. And here, we get the plus operator that doesn't have this commutative property because if we swap the operands, we get something different. So Python is somewhere in the middle of the spectrum where we, Pythonistas, believe, is a reasonable strictness of the language. But of course, the idea of reasonable is subjective. Then we have duck typing, something we are very proud of that we have this nice concept of duck typing. So in duck typing, protocols are implemented implicitly. We need to implement the required methods and the object becomes compatible with a protocol. We have this other way of typing where classes must inherit for another class or being marked as implementing the protocol. How do we call this? We could call this actually my idea platonic because Lata was this guy who had this concept of metaphysical ideas. And he wouldn't agree with this proverb that if it looks like a duck and quite like a duck, it's a duck. Lata would say that there exists an idea of a duck. And when we see a duck, we recognize it because our brains, our minds, somehow know already this idea of a duck. So it could be compared to classes. Or if we don't like this whimsical ideas, we could say it's structural typing and nominal typing. And these are the terms that are used in myPy documentation. So they would probably be somehow a part of this phytonic lingo, just a short regression because before, it was set interfaces, but it was crossed out and they said protocol. There is this idea that we, Pythonistas, tend to use a language that is a little different from programming in other languages. For example, we say race, where all the other languages say throw. We call a race list. When others say list, we say deck. If others say it's a blade and abuse of exceptions, we say stop iteration. And also, the thing that in most languages is called interfaces, we call it protocols. This is a little problem when we want to communicate with others, but we have to remember that this name protocol means the same that interface in Java or in Golang. There is this misconception also about duck typing being some special type of dynamic typing. It is not true. Static languages can be also duck typing. And we can see an example here from Golang. We have this interface, duck. We have a struct called malart. It implements these methods. And we can use an instance of malart as an argument for a function that requires a duck. Nowhere here, we say explicitly, a malart is a duck, but our compiler understands it. And there is also fifth criterion that we need to take into account. It's three versus six attributes, because we are working with the data structures, and we also need to take this into account, although it is not actually a paradigm of typing. It's how objects work internally. So if we look at our typical stack, we see that the layers of the stack exhibit a different way of typing. Python and JavaScript are not that different here. Of course, JavaScript is much looser than Python, which I showed before. But for SQL, it is completely different. We have loose typing. We have static. We have fixed attributes, because we have, of course, the tables that have fixed columns. We don't have any idea of interfaces. So if we communicate from Python to SQL, we get this inherent incompatibility. But this incompatibility was bridged long time ago with various ORMs. ORMs are actually a little un-Pytonic, because they use fixed attributes, something which was not a part of a classic Pytonic way of writing code. They don't use any interfaces. If you want to put something as a related object, you have, of course, to put the right class. You can just re-implement a model by implementing the same methods. But this bridges the gap between Python and between SQL. And it also fixes some problems we have with SQL. If we look at this operation, we can see why I declare SQL a weekly type language. Here we are taking an ID of a publisher and set it as an outer ID of some book. This operation is correct, but it doesn't make any sense. And the result of this operation is actually undefined, because we don't know what would happen. It depends if there happens to be an author in our database that has the same ID as our publisher. Then we would get a value that doesn't make any sense. If not, we would get an error. So this is something similar to what we get in C. Sometimes we get gibberish, and sometimes we get segmentation fault, but we can't really say in some situations what we would get. This is actually bridged by ORM, because this operation would always raise value error. So ORM would make sure we aren't doing something very stupid. And this is one of the benefits of ORM. But this is something that we have for years now. And how about annotations? So let's enter mypy into our stack. So mypy is a strange thing, because it uses static analysis of a dynamic code. And it is somehow disturbing for many people. And some people say that it's un-Pytonic, although actually mypy can give us some benefits. So now let's look how Django and mypy can work together, how do they work together correctly, or are they lacking something? So now let us look at some example models. And here we have a book. This book is linked to a publisher and to the author. We allow the author to be null. We don't allow publisher to be null, because sometimes we cannot label a book with the author. And if we now look at some functions, some functions, these functions almost all have some problems. And if we run mypy on this code, it would actually catch some of these problems. It would catch that we are using incorrect type for our decimal field. OK. It would catch that we are using publisher man, but not that author man. OK. It would also catch the thing that I was mentioning before, when we are trying to put the value that is not pointed by this foreign key. It's also OK. However, these two things are also not OK, but they are not cut here. We are creating a book that has only a price. This would cause an error, because we are setting null values to null columns. And this actually could be fixed. And maybe it would be fixed in the Django steps in the future. But here we have the same problem. And this can't be fixed, because that's not how mypy works. Mypy is going line by line. And in order to know that this is wrong, we have to analyze the whole code as a whole. So it wouldn't work anyway. So what about mypy? It already recognizes the relationship between column types and Python types. It recognizes the nullability of fields. However, it can't handle some problems when we make incomplete objects. And it also requires a mypy plug-in. That's why it's not universal. This would only work with mypy, not with any tool that uses type annotations. So this is also a problem. OK, now let's look at the other side of our stack at the communication between back-end and front-end. I said that JavaScript is not very different from Python with regards to the typing paradigm. However, we also need to consider JSON. And JSON has almost no typing. It has only primitive types and no information about classes or something like this. So in order to introduce some more type safety, we need to add additional tools. So we would use OpenAPI3. And we would substitute JavaScript with TypeScript. TypeScript is something that a JavaScript developer can learn in several hours, because it's just JavaScript with type annotations, just like type annotations in Python can be also learned by a Python dev. And OpenAPI3 is a system of schemas that can be used for tests. And they can be also used for code generation. So now let's look at the other part of this. So we created here a simple React component. And this React component simply displays the data of a book. However, the fetching of the data is a little more complicated because it uses several API endpoints and combines this data. And it has to handle the situation where the author of the book is known and not call this endpoint without a reason. And if we now run this, we would see that it is working just like we wanted. It would call the backend, and it would display all this data. However, what if we have a developer that is careless? What if we have a developer that gets this information? You need to allow for known publishers. So this guy goes to Models and he does the simplest things on Earth. So he just allows it to be known. And if we don't introduce any type safety mechanism, then it would probably call some bugs that would happen in various places because not all of our code can handle the null value. However, here we would have to do one more thing. This would be done by this guy who does it or by CI. And this would be two things. First of all, it would be using this tool to generate a schema. It is called spectacular. There is also a schema generator in Django REST framework, but it is not as good as spectacular. And we would generate a schema game file. Then we would go to the frontend. This is all things that can be automated. And we would generate some more files. That would be typescript files. And we can see that all the types are generated here. And now, if we try to run our code, we would see that during the compilation time, during compilation from typescript to JavaScript, we already get an error because here typescript detects that the ID of a book publisher needs to be a number. But that book publisher can be undefined for null. So we get what we wanted. We get the situation where the CI detects our error. And the developer can now see that he broke the contract and he needs to handle this null value. So what are our takeaways from this demo? There are tools for code safety enforcement that we can use in a whole Python stack. And they are worth consideration. And the benefits, I believe, from them for any big project, outweighs the costs of introducing them. However, they are, of course, not perfect. We can't expect them to catch all the typing errors. And we probably, due to this inherently dynamic character of Python, we would probably never be able to do this. However, we can do a lot with them. What can we see in the future? I believe that one thing we need to expect or maybe work on is more patterns in type annotations that are available without plugins. So we can actually make type annotations that would work in every system, not only in my Python. And the other thing is tools that are based on code annotations, not on the scripters. This is how they work, like data classes. There are two projects like this. It's Strawberry GraphQL. We will have a sprint of Strawberry GraphQL on this conference and Data Class DB. And they also try to do the same thing that Django REST frame or Graphene or ORM, but in a different way. So thank you. I see that we don't have that much time left. So I believe I would have to answer any questions in the breakout room. Is it correct? Yeah, that's correct. Thank you very much for your talk. At the moment, you're still in the Brian track chat, but anybody who has a question for you can then move to talk full stack type safety, where you then can answer more complicated questions if anybody is not totally stumped by this by now. Thank you very much for taking the time to show us all this. Oops, that was not a proper loss. So we'll try this again.