Brought to you by Michael and Brian - take a Talk Python course or get Brian's pytest book

Episode #195: Runtime type checking for Python type hints

Published Tue, Aug 18, 2020, recorded Wed, Aug 12, 2020.

Sponsored by us! Support our work through:

Michael #1: watchdog

  • via Prayson Daniel
  • Python API and shell utilities to monitor file system events.
  • Example:
        observer = Observer()
        observer.schedule(event_handler, path, recursive=True)
        observer.start()
    
  • Watchdog comes with an optional utility script called watchmedo
  • try $ watchmedo log and see what happens in that folder.
  • Why Watchdog? Compared to other similar libs

Brian #2: Status code 418

Michael #3: pydantic’s new Validation decorator

  • via Andy Shapiro
  • Built-in type checking for any function via a decorator
  • easy to add for any public methods in a package
  • pydantic uses lots of cython under the hood so it should be fast....
  • The validate_arguments decorator allows the arguments passed to a function to be parsed and validated using the function's annotations before the function is called.
  • Under the hood this uses the same approach of model creation and initialization; it provides an extremely easy way to apply validation to your code with minimal boilerplate.
  • Example:
        from pydantic import validate_arguments, ValidationError
    
        @validate_arguments
        def repeat(s: str, count: int, *, separator: bytes = b'') -> bytes:
            b = s.encode()
            return separator.join(b for _ in range(count))
    
        a = repeat('hello', 3)
        print(a)
        #> b'hellohellohello'
    
        b = repeat('x', '4', separator=' ')
        print(b)
        #> b'x x x x'
    
        try:
            c = repeat('hello', 'wrong')
        except ValidationError as exc:
            print(exc)
            """
            1 validation error for Repeat
            count
              value is not a valid integer (type=type_error.integer)
            """
    

Brian #4: Building Python Extension Modules in Assembly

  • Anthony Shaw
  • From twitter announcement:
    • “After a series of highly questionable life decisions, my Python extension written in pure assembly is now on PyPI. https://pypi.org/project/pymult/ it required writing an Assembly extension for distutils, I also added GitHub Actions support so its running CI/CD and testing with pytest”.
  • A proof-of-concept to demonstrate how you can create a Python Extension in 100% assembly.
  • Demonstrates:
    • How to write a Python module in pure assembly
    • How to write a function in pure assembly and call it from Python with Python objects
    • How to call the C API to create a PyObject and parse PyTuple (arguments) into raw pointers
    • How to pass data back into Python
    • How to register a module from assembly
    • How to create a method definition in assembly
    • How to write back to the Python stack using the dynamic module loader
    • How to package a NASM/Assembly Python extension with distutils
  • The simple proof-of-concept function takes 2 parameters,
        >>> import pymult
        >>> pymult.multiply(2, 4)
        8  
    
  • May need a few more test cases:
        >>> pymult.multiply(2, 3)
        6
        >>> pymult.multiply(-2, -3)
        6
        >>> pymult.multiply(-2, 3)
        4294967290
    
  • Also, clearly Anthony has too much time on his hands. Just saying.

Michael #5: easy property

  • via Ruud van der Ham
  • The easy_property module, developed by me, offers a more intuitive way to define a Python property with getter, setter, deleter, getter_setter and documenter decorators.
  • Normally when you want to define a property that has a getter and a setter, you have to do something like
        Class Demo:
            def __init__(self, val):
                self.a = val
            @property
            def a(self):
                return self._a
    
            @a.setter
            def a(self, val):
                self._a = val
    
  • IMHO, the @a.setter is a rather ugly decorator, and hard to remember. And there's no way to not define the getter.
  • With the easy_property module, one can use the decorators
    • getter
    • setter
    • deleted
  • as in:
        Class Demo:
            def __init__(self, val):
                self.a = val
            @getter
            def a(self):
                return self._a
            @setter
            def a(self, val):
                self._a = val
            @deleter
            def a(self):
                print('delete')
                del self._a
    
  • In contrast with an ordinary property, the order of definition of getter, setter and deleter is not important. And it is even possible to define a setter only (without a getter), just in case.
  • With easy_property, you can even create a combined getter/setter decorator:
        Class Demo:
            def __init__(self, val):
                self.a = val
            @getter_setter
            def a(self, val=None):
                if val is None:
                    return self._a
                self._a = val
    
  • Finally, it is possible to add a docstring to the property, with the @documenter decorator:
        Class Demo:
            def __init__(self, val):
                self.a = val
            @getter
            def a(self):
                return self._a
            @documenter:
            def a(self):
                return "this is the docstring of Demo.a"
    

Although this might not be always a good solution, I think in many cases this will make it easier and more intuitive to define properties.

Brian #6: Non Blocking Assertion Failures with pytest-check

  • Ryan Howard wrote an article about a project of mine on the TestProject blog.
  • I think it’s a first that someone else wrote an article about something I made. So that’s cool.
  • Most tests do the “check” part with assert statements.
  • The problem is assert stops after the first failure and you often want to check lots of stuff, and you want to see all the failures.
  • Ryan has a good example with checking web pages using selenium and a simple example of wanting to check both the content of an element on the page, and the url.
  • Cool use of pytest-check
  • See also:

Extras:

Brian

  • PSA: There are no capital letters in pytest, even if it begins a sentence.

Michael

Joke:

  • XKCD git - xkcd.com/1597
  • “I used to do low-level programming. Then a product I bought told me, "No assembly required." Since then, I've been coding in Python.” - From Rueven Lerner, Inspired by Anthony Shaw

Want to go deeper? Check our projects