Error Handling
Currently, there are two different ways that errors are being handled. One is by throwing an Exception, the other is by printing an error message and returning None.
The latter is undesirable both due to requiring explicit propagation of errors even when they are not actually handleable, and also because Mypy does not handle Optional very well (as returned values at least). If we're going to use Mypy and type hints it would be better to avoid having functions return Optional types (or any sort of Union types) and use Exceptions for all error handling.
I propose creating an Exception subclass for any handleable exception, which then can be caught if it is possible to handle it (possibly printing a warning message), and then at the top level have an exception handler that catches all exceptions and prints error messages. It would also be useful to have a debug flag that can enable stack traces for exceptions that are uncaught except at the top level.
Also note that there are also points within the code where we can handle all possible exceptions, such as when loading pybuilds, as the best way to recover there is simply to abort loading the pybuild, print a warning, and continue as if the file didn't exist.