 Thank you I'm very encouraged to see this many people care about testing Swide, not everyone would show up Thank you for coming. Yeah, I'm Alexander. I'm a software engineer. I work at Palo Alto Networks. This is a cybersecurity company and This talk is about Sorry, give me one sec This talk is about my love affair with pedantic and ultimate rejection I Really really liked pedantic and I definitely feel like I had a crush on the source code when I first Found out about it and So I really wanted to contribute to try and be part of the project But I when I had a look at the project. I realized it It wasn't really the right time to to contribute They had kind of shut it down and so I had a look at some other projects that the author had written and I found one called dirty equals So this talk is going to be about my story of contributing to dirty equals and things that I learned from that But just to kick things off in case you don't know what pedantic does this is like a really simple minimal example and I want to focus a little bit on how Samuel Colvin who wrote by Dante thinks about code because that's that's kind of the thing that I learned the most from contributing to his projects and I think What he does is takes things that are internal to Python and changes them in a small way in a library That has very interesting results so in pedantic Sorry, I don't know how to change that, but I'll just I'll just keep going He is very good at Yes, right here. He's made Python check type hints at runtime so you can you can see here that That B value to my model is sorry the a value I mean it's supposed to be an integer but later when it says My model a equals 100 I've actually passed it a list of integers So I then get an an error which says Yeah, this works That it should be an integer, but you've actually given it a list of integers So this is this has changed how Python normally works type hints are normally just type hints They're just hints and they don't get checked at runtime But if you use pedantic your program will actually error if the type in is wrong So something does happen at runtime and that this kind of paradigm of making stuff Change internally to Python in it in a sort of contained way is what I think is really cool So but just first of all I wanted to talk a bit about what happens when you come across a new open source library And how to figure out if it's worth contributing to since I think people don't always talk about this But it can save you a lot of time if you don't do this I think you can do a lot of work on a pull request and then nothing happens, which is quite disappointing So this is sort of my checklist the first thing I would check is One was the most recent commit Hopefully it was a merge commit So as someone's merged in a pull request from someone else rather than it just being the maintainer of the library adding their own code I also like to see what the activity is like on the issues and get a sense of what's happening with the library and then I'm not gonna name any names, but on some repos. I've seen really really frightening responses from maintainers and I think life's too short So these are things I think are worth checking before you Make a open source code contribution But yeah on to dirty equals which is The the project that I did end up contributing to after running some of those those checks on pedantic Dirty equals is a little bit like pedantic for testing So if you want to do cool different things with your tests, you this library is good for you It ultimately helps you make tests that are easier to write. I also think a bit more fun to write I I personally find writing tests can often be a bit boring and tedious to me This is a way to add some syntactic sugar that makes it a bit more fun and It fundamentally lets you misuse the equals operator in python. So just like pedantic let you check Type hints at runtime. This is the kind of trick pulled in dirty equals it Changes the way that equality works in python Which is what it says here on the on the home page of the library in that box at the bottom And It's really useful when you're testing responses back from API's which I'll explain in a bit more detail later But I think probably a lot of Python developers work with API's and so that's kind of one of the main use cases for this This is just a very simple example of the library syntax and Yeah, here you can hopefully see how the quality is getting misused we can write a really simple test case and Do these sort of unusual checks with new objects from dirty equal equals we can check In the first line there does does the list have length 3 and then does it contain the string a and you can do This with quite interesting syntax with like an ampersand there a pipe operator and not equals and you can chain and combine these so this is a simple example now, but hopefully you'll see as we go through the talk how you can kind of build on this and Make it all a bit more more complex then This is probably the kind of pattern you'd be most likely to use In actual production code where you are writing an API test You here are gonna maybe mock out a client and then test that some JSON is equal to something like this and I think hopefully here you can see how versus Just kind of testing this is exactly equal to the kind of response You're expecting which is what at least I used to do before discovering this library You can write much more dynamic test cases in a way that's quite readable using these dirty equals objects so like Here on that avatar file line you can check that the string matches a particular reject So I think that's probably a nicer pattern for testing It's also gonna make things arguably a bit more modular if you want to reuse some of these Dirty equals objects when you're expecting these kind of responses and you get a pretty nice diff in pi tests So this is an intentionally failing test where I gave it the wrong The wrong value for the avatar file and you can see in the diff you get the dirty equals object back for me This is way easier to read not right now Normally it's way easier to read than just seeing like a pretty long pi test trace back with Some some regex that I'm not really sure where it is So I think the advantage of making things more declarative and explicit is that when you get around to failing tests and have to fix things You get these quite nice readable objects, so How does this really work though under the hood this if you think about it? This is this is like this feels like Bad python. It's not bad python. I wouldn't be giving a talk on it, but Normally equals equals is a strict equality check. So Like that second line there should definitely fail. Hello, definitely does not equal true for a whole bunch of reasons But if we use this it's true like thing from dirty equals it it passes because it's it's a truthy string it's not an empty string so Something funky has happened in order for this to take place and that's what I'm gonna kind of move into For the rest of this talk, I'm gonna get a bit into how a quality actually works in python Which I did not know before contributing to this library So it turns out that when when you have a statement like X equals equals Y in python The first thing that happens is that X checks Can I compare itself to Y the thing on the right? And it calls the done the equals method, which is what you can see up here at the top And the important thing from this slide is that this never this code never runs Which is why if you if you run this Y equals doesn't get called But and the reason for that is that this returned true here. So it exits here This might make more sense if you have a look at this slide So in python equality is designed such that if you return not implemented from the thing on the left It then proceeds to check equality at the thing on the right So here we have X equals equals Y the first thing that fires is done the equals here You return not implemented in python if you you don't know how to compare yourself to Y So X is saying if I don't know how to Check I'm equal to to Y if Y was an integer here rather than some weird class I wrote there would be a quality logic here to check if it's the same the same kind of integer Assuming X was also an integer, but The the really important thing here is that Y equals gets called so That basically means if we write a custom objects in python Which is what all the dirty equals objects are things that aren't in the standard library Then whatever we write in this done the equals method here Is going to control how the equality logic works so using this approach we kind of get to hook in to how to do equality checks in python and This is from the C python source code for how pure path works Just to kind of show you how what a sort of good blueprint by someone who knows python a lot better than me looks like for this So if a pure path object is trying to compare itself to another object pure path is designed so that it Doesn't know how to compare itself to anything that isn't a pure path object So this is basically saying if it's if it's pure path then go here and do the pure path kind of comparison that Is all good, but if it if it's not the object is getting compared to if it's not pure path then return not implemented So then we go to the object on the right and check If that knows how to compare itself to the object on the left, so Just to wrap this up. Hopefully this makes sense now This is the kind of flow chart of what happens If X knows how to compare itself to Y It just does the comparison straight away if it doesn't then we see if Y can compare itself to X and that's This bit here is what I'm going to dive into next Why would we care about this this when I was first looking into this seemed pretty weird equality works well in python Why would you want to change it? But you can now use this to Write your own comparison logic and make things give back that they're equal or not equal based off your own logic so a really simple example of that is here and The dirty equals library is a little bit more complicated than this, but this is just some kind of hopefully clearer examples to follow So this is how you would create a class in python that you you say Is equal to the object on the left if the object on the left is bigger than five So this this other argument here that gets passed to the done the equals method is Is the object on the left? So that's why two equals equals X here is false and Six is true because obviously six is bigger than five You can also do this for not equals so the same kind of logic that I've been talking about also works for not equals so the idea here is Beyonce is only equal to herself. So Hello is not equal to Beyonce. You too is not equal to Beyonce But It is true that Sorry, it is false that Beyonce is not equal to X because Beyonce is indeed equal to herself so this is a More realistic example for how you would do something like the is is I don't know how to say this out loud is stir Is str class that was in dirty equals? So here when you when you build the class You give it a rejects argument and then in the check I'm catching the case where it's not a string because This from the name hopefully it's going to be obvious to people that it's only for a string I'm going to raise a value error But if it is a string then I have my own logic here to basically say yes, it's equal if there's a rejects match But otherwise it's false. So that's that's the kind of core idea of how you would implement this in the next slide trying to work with these dramatic pauses This is how we would use it. So this rejects here is basically saying alpha numeric only which means that I get false for Only this one at the end all the other ones are alphanumeric strings. So they pass fine. So and This is yeah, we've kind of done it now We've done our own way of writing an assert statement with the quality that does really really funky stuff Just in case it's still feeling very confusing This is the kind of core logic for how this all all worked and Next you might be wondering, okay This this kind of seems fun. It seems interesting, but would I actually want to use it at work? So I thought I would give you a concrete example of some tests that I've rewritten using this that have made my life easier because I work in in cyber and I Often have to well not in this job in my old job We've had a API people were paying for when we were sending back data like IPs and hashes and it was pretty important We'd send back the right kind of hash or make sure that an IP was valid Otherwise, obviously people are paying for it, but also it would break things and This I think is a much nicer way to check an API response. So suppose you get an API response back that has Jason on the return to a dictionary that has a hashes key and IPs key Well, I worked often we we had mixed char256, MD5s and char1 hashes But we now would sort of transitioning to only send back char256 is but often there were some bad hashes in the response So this is really easy to test in dirty equals and by a bad hash I mean one that's not the right kind of type You can just do here fail the test if The list contains a MD5 hash and also check Just what that was That it contains a char256 hash and Then another thing that I think is pretty cool is The way this library is designed. It's really easy to create your own logic to run these tests So suppose you you don't like his hash or you don't like his IP for some reason And then you want to do your own stuff You can use this function check class here And here you just pass it a callable. So here check IPs that I've defined above And then you can do whatever logic that you want here here. I've just done a Generator comprehension that basically says make sure everything in the list is an IP So this will fail if I had a any kind of invalid IP here But there's a there's a lot more sort of complex stuff that you could build up here And this is for me where the writing test bit becomes a bit more fun This is I think quite a lot more fun than just normal sort of I Don't know I can't only be bothered to write IP validation logic in most of my tests personally maybe everyone else is more diligent, but That's that's kind of what it would like at work And so I'm not gonna recap on some of the things that I Took away from this and I hope might be useful for you after that I'm gonna move on to a couple of other kind of fun things that I think you could take with some of the stuff I've discussed so The main thing for me was if you if you really like someone's code style or their way of thinking about code to try and get involved Try and contribute to one of their projects Even if one isn't active For me the main thing I learned doing this was that it is kind of okay to change python internals if you do it intelligently and I feel like I learned from this Code base how to do that well, and I don't think I would have learned that if I hadn't contributed to it and I Think there's a lot of other stuff you could do with that So I was gonna show you an example next about how pathlib actually does this I gave a dry run of this talk to some some colleagues and they told me pathlib does something similar, which I had never really realized While this loads pathlib basically overrides the dunder true div method in in python, so It kind of hack hacking is the wrong word. I'm gonna stick to overloads here True div is what happens when you'd say something is divided by something else It'd be a bit clearer on the next slide, but it's in path there It basically says if you're dividing an object by another object just join the path Which is how you get She's gonna do this slide first really cool syntax like this so You get This whole path gets put together by doing this cool Whereas this operator normally in python would mean you have to divide stuff So I think this is a really nice way to do pretty cool syntax tricks and This is a really good little example in my opinion in pathlib But I think this kind of approach is something you could definitely put into other libraries And I also think it's quite a cool feature of python that you have these dunder methods that are exposed and you can hook into and change things One last thing that I took away from the the workshop on pie test a bit earlier is using hypothesis for testing and I think this would play quite well with dirty equals as a combination So here's just like a very quick example I put together of how that could work if you're not familiar with hypothesis What it's basically doing here is saying given some hypothesis Give me a random bunch of numbers within this range 100 to 102 10 to 12 and Apply them to these arguments x and y. So this is kind of the same syntax as parameterizing fixtures in python But the difference is that you don't specify the parameters instead hypothesis gives you a bunch of random data So the idea is that you Have a more robust test since you might be checking for more edge cases But I think if you then add in some dirty equals logic. So like here I've said Sorry that if Is a proxy the dirty equals class that lets you check a number when they're within a range So it's going to say approximately 110 with like Delta of four so max 114 106 I Think that's quite a nice combination I think that lets you kind of have a bit of breathing room around the random stuff that gets input and I often if I am on a bigger code base I write a ton of fixtures and parameterize them and it gets really messy and I thought this is quite a lot cleaner So yeah, that's pretty much the end of the talk. I'll share the slides afterwards here a lot of the resources I use for it including some of the source code links if you want to kind of dig in and really get into it And I think I will finish there. Thank you very much for listening Thank you, and I admire how you were calm when the slides were not working. I would freak out I also have a question actually So I use pytest a lot and if you assert that something is in the list for example And the assertion fails it will tell you nicely this item here was missing on Index three or whatever But if I always just assert equalities with custom equality methods How do I propagate more information to the actual developer who's running the test that why the equality failed Other than returning through or false or not implemented Yeah, that that's a very good question. Um, I think it's a bit of a paradigm shift with this library Is that you you don't get that kind of pytest trace back So you actually get a really unhelpful error unless your dirty equals object is very clear So if if you did like list equals equals contains Uh x and then your list didn't have x in the test failure message It will say it failed because it didn't contain x So I think that's pretty clear to read but If you if you wrote a messier dirty equals object like it should just contain strings Then it's not very useful and one thing I should say is that if you use dirty equals It doesn't stop you from using everything else in pytest So you could you know if it makes more sense to do the pattern you described you can just use that for that test Thank you Hi, um, I have maybe a bit of a stupid question, but considering all this Magic methods that you mentioned could we go even more postmodern and do it like dirty Subtraction dirty power dirty multiplication like doesn't have to be equals, right? Yes, so you could but you you probably have to be smarter than me So I I mucked around with them after doing this talk and it it was kind of harder than I thought but um I think the true div example is like a a simpler way to do it Where you can you don't have to get into the logic of the order and not implemented Um, you can just overload a method. So I don't I don't know what the dunder method is for multiplication, but I mean, why would you want to do that out of curiosity? Why would how would you want to change just because it's fun? So if you if you want to do that, I would recommend having a look at the python like object operator docs There's a there's what's one of the resources in my slide where it has all the dunder methods That you can overload also if you find um maxim danilov He has a very cool project where he's doing something similar to that. Yeah Let's give a big applause to alexandra