 So Prashan is going to be talking about writing good Python. That's really good. Okay, so thank you for joining. I didn't ask you, is this your first a Python? Yeah, it's my first Euro Python, but I already presented it at PyCon Limerick, so. Okay, okay, welcome. Thank you for representing. And yeah, good luck with your talk. Okay, so let's start this talk on writing good Python. But Python is already great. It is readable. It has massive ecosystem of libraries, frameworks and tools. It is used in machine learning, data analysis, ever scrapping, web development, IoT, et cetera. It has a vibrant community and the list goes on. But I'm going to talk about the writing part of it. About me, my name is Prashanth. I'm currently a software engineer at HubSpot Dublin. The idea of this talk comes from my experience working at a small startup. They're a small company, but fortunate enough to be working with big clients. Some of them are amongst Fortune 500. We were writing automation systems in Python. So when you are writing code for a process which is done by a human in such large organizations, you have to be extra careful because machines don't double check. They do what they're told to do. Like this scenario and in general, our employers expect us to write high quality code. So what is high quality code? So first thing is it should be defectless and second thing is it should be maintainable. Maintainability can be roughly defined as inverse of amount of time it takes a developer to make a change and risk that change will break something. It cannot be strictly defined. It is also judged by the readability, coupling and consistency of the system. Today I'm going to deep dive into writing Python code with consistency. So a consistent code follows a style. It don't have any hidden surprises. A style guide is mostly cosmetic, but it helps us in avoiding some common logical mistakes and make our system more maintainable. So let's start with an example of bad or low quality code. So this is a highly opinionated and simple example of a hierarchy calculator. Some of you might have seen something worse. Some of you might be okay with it and for some of you, this might be a perfect code. But for me, I don't want to be a developer who is maintaining this type of code base. There are some obvious red flags such as inconsistent naming convention. We can see the name of classes in small cases. We can see that in some places the method arguments are in block letters, et cetera. There are unused imports and in the init function, you can also see a dangling semicolon. Maybe the intern working on it was a Java developer. Apart from this, there are some obscure bugs such as on the second part of the page. In the accept blog, you can see a user sys.maxint. So this variable is no longer available in Python 3. So is there a way that we can catch these bugs and inconsistency before then? So Python is a language that comes with batteries included. So it comes with a style guide written by none other than the creator of language itself. It is known as PEP 8. PEP stands for Python Enhancement Proposals. So these are design documents for Python community which provides information about new features of Python environment or its processes. There is another style guide for Python doc strings known as PEP 257. So now we have PEP 8, but how can we enforce this in our code? So pylint is a library which can help us in this. So pylint can help in enforcing coding standards. It can detect errors. It can suggest refactoring and also very customizable. You can look more about it on its webpage which is mentioned here. So let's run pylint on our previous example. You can see pylint is able to detect so many errors ranging from unnecessary semicolon to unused variable or missing function or method doc string. It is also given a score to our code. It is giving us 1.62 out of 10. So this code is far from perfect. You can see list of all checks done by pylint in the mentioned GitHub depository. There are hundreds of checks that can be done before and so to be helpful in code reviews. There are also other alternatives to enforce PEP 8 standards such as flake it and pyflakes. So we have updated our code and fixed all error messages. You can see at the bottom of screen that pylint has rated our code 10 on 10. This might not look impressive, but when you are working in a team of developers, this kind of tool is very handy. So I mentioned something named PEP 257. So we have seen that pylint is able to detect empty or no doc strings, but sometimes we want to enforce more standards for Python doc strings. PEP 257 can help us in that. To enforce PEP 257 standards, we can use a library known as PyDocStyle. So as you can see that pylint was giving our code at 10 on 10 rating, but PyDocStyle is still complaining. There is further room for improvement. So if we use PEP 257, it and not only make our code more consistent, but we can also use another libraries to automatically generate code, such as the oxygen pydoctor or Sphinx. So everything looks fine till now, but can we make our code more consistent? So let's see another example. So here we are trying to use a calculator. So in the second print statement, you can see we are trying to add an integer to a string which will throw an error because it is not permitted in Python. So is there a way that we can give a heads up to a developer that what kind of arguments does a method expect? So there is a library known as mypy which can provide optional static typing for Python. It is based on PEP 484 standards. And the best thing is that there is no runtime over it of using it. So I have type annotated one of the methods from the previous example. So to do this, you can see that I've mentioned the type followed by a colon for arguments and for the return type, it is followed by aero sign made using a hyphen and a greater than sign. And in the bottom, in the console screen, you can see that when I run mypy it will, it can detect that we are trying to pass string where a float was expected. So we can see that it is pretty easy to type annotate primitives, but what about other types such as this or like because in Python, you can also functions. So how to type in that? Python has a module name typing which has many classes such as tuple, callable, list to type in those corresponding types. You can also create your own types. I mentioned that I mentioned that mypy don't have any runtime over it, but sometimes this is very tricky. For example, you can see on the right part of the slide there is an example where we are trying to type annotate a method argument named obj by a class a, which is imported from another module. So sometimes like there are some operations which are performed as soon as we are trying to import a module. So this import for only the sake of typing is costly. Sometimes we want to avoid that. So there is a special constant name type checking in the typing module, which is true for third-party static type checkers, but it is false at runtime. So if you use that variable, then during execution this module is never imported. So we have pylint pydoxtile and mypy under our belt. Can we make our code more consistent? So there are two more libraries, bandit and black, which can help us in achieving the consistency which we are trying for. So bandit can detect security issues in the Python code. There is a long list of all the checks which it can do to improve the security of your code. You can see the list of examples in the mentioned GitHub repository. Black is a source code for matter. It is very useful for source code uniformity across the team. So we discussed lots of tools here, but how can we integrate them into a development lifecycle? So according to me, they should be used while writing your code before committing and after pushing. So you can use ID integration of these tools to highlight mistakes while writing your code. You can use Git pre-commit hooks to run these tools before committing and you can use them in your build pipeline so that they run alongside test cases. So I'm going to discuss more about how to use these tools as a pre-commit hooks. So instead of manually managing them, we can use a Python library known as pre-commit to use pre-configured Git hooks. So all the configuration is done using a YAML file. So you can read more about the project on its website and you can see all the list of available hooks. So this is a sample configuration file to use pre-commit. Fortunately, all the tools which we discussed are available to be integrated as pre-commit hooks. This configuration will seem obscure to you but after like going through a documentation, you can get it like it is very easy. We are mentioning the repo which is hosting our hook, the REV is the revision or the branch which pre-commit should look for and ID is the ID of that hook. So if you put this normal configuration in the root of your project and then pre-commit install, it will install all the pre-commit hooks for you. So now every time you try to commit your code, it will run all these tools and if any of these complain, it will not let you commit. So sometimes if you just want to check that whether these tools are passing or not, you can use pre-commit run. So I'm going to show you like how to run pre-commit. So this is a sample project. So this is our first example which we discussed. You can see that PyCharm is able to highlight most of the errors. So you can see that these errors are highlighted like unused imports and the inconsistent naming conventions and like, so the variable which is not present. So this is the example after running, after running pylint. So you can see that pylint is not like strictly enforcing a standard for doc string and it is also not able to detect this error where we are trying to add integer and string but in the third example, I have added some doc strings according to PyDoc style conventions and also type in all my functions. So you can see that PyCharm is able to detect that, so let me try to run this. So okay, so you can see that I have this .pre-commit .config.iml file, so this file should be present at the root of your project to, so that pre-commit can configure these hooks for you. So you can see I have like configured pylint, PyDoc style, mypy, bandit and black. So you just, if you run, you think if you use pre-commit run command, it will run all those tools for you. So you can see it is detecting, so pylint is failing, it is detecting all those errors. You can see PyDoc style is also detecting some of the errors relating to doc strings. Mypy is detecting that error relating to string and float, black is complaining, and we don't have any security issues. So you can fix it and then everything would be passed and you can commit your code. So lastly, if you ever have doubts about how can you write better code, you can see the Zen of Python by Tim Peters. You can spin up a Python console and write import this. So you will be presented with a list of messages or suggestions to write a better Python code. So thanks all for listening to this talk and I wish you luck on your journey to be a better Python programmer. Okay, thank you very much. So we have time for questions, so first question is, sorry for the attendees, if you click in the Q&A, you can write your question, or if you want to ask the question live, you can click the raise hand button and I can enable the microphone. So first one is from Thomas saying, how do you deal with exception to rules? Where to draw the line? Yeah, so like most of these libraries, most of these libraries comes with some kind of configuration. For example, like in Pylint, you can use a file known as Pylint RC, where you can skip all the rules which are not in line with your organization or your team. So every library has some kind of a configuration where you can mention these exceptions. Okay, so any other questions? We have time, so if anyone, okay. Cool, then that's all. Thank you very much. Thank you for presenting. Yeah. See you next year. Yeah. See you. Thank you.