This is the exact opposite? They explicitly encourage doing resource-opening in the __enter__ method call, and then returning the opened resource encapsulated inside an object.
Nothing about the contract encourages doing anything fallible in __init__
It is a tragedy that python almost got some form or RAII but then figured out an object has 2 stages of usage.
I also strongly disagree constructors cannot fail. An object that is not usable should fail fast and stop code flow the earliest possible. Fail early is a good thing.
There is no contradiction between “constructors cannot fail” and “fail early”, nobody is arguing the constructor should do fallible things and then hide the failure.
What you should do is the fallible operation outside the constructor, before you call __init__, then ask for the opened file, socket, lock, what-have-you as an argument to the constructor.
Fallible initialisation operations belong in factory functions.
The real problem is that constructors and factory functions are distinct in the first place. They aren't, in Rust, and it's much easier to reason about and requires far less verbiage to write.
You can make globals thread safe by using thread locals. You can make methods using them reentrant by carefully saving and restoring state. What about exceptions? Any exception from `process()` is going to leave this global state in a total mess.
I should have talked more about that in the article. I mentioned defer, making functions reentrant, etc, but languages with exceptions and without defer can make things much harder. I tried to make it clear the global state should be accessible from a handful of functions or within a file/module
100% agree with the AI on @cache decorator. It's a footgun and should never be recommended without a proper disclaimer. Unless it's a simple "single shoot" script, you really do not want the cache global like that.
Object oriented programming is not about defining classes. It's about using objects. You don't have to define new classes to do OOP. Just use existing classes and objects polymorphically!
All those calls are dynamically dispatched - the essence of object oriented programming. This is what allows you to not worry about:
* which string implementation `url_layout` uses
* which HTTP protocol, encryption, authentication, chunking `resp` uses
* what database is connected to `session`
You cannot avoid using objects - that's how all modern operating systems work.
Using classes without the need to call them polimorphically just as a nice namespace for methods is a separate issue.
The problem with scp is that the trust also needs to go the other way. There is a lot of ways ssh server can trick the client into doing bad things on your local machine.
Mocks have to be updated and fixed every time you do any meaningful changes in production code. Good fake can be reused in many tests and will keep behaving like a real thing with minimum maintenance. Much less complex than maintaining hundreds of mocks each implementing different parts of the interface.