So, brace yourself, it's a little bit complicated (don't hesitate to reach me for more explanation).
So, it goes like that:
- I've introduced the usage of the
@wrapsdecorator from functools.wraps in logilab.common.deprecation.callable_deprecated because this is a good practice
- turns out if broke francearchives code
- which is because it uses an old version of CW
- in this old version of CW, logilab.common.modutils.LazyObject is used (well, inherited) here https://forge.extranet.logilab.fr/cubicweb/cubicweb/blob/3.24.0/cubicweb/schemas/__init__.py#L51
- LazyObject all you to do something like
exists = LazyObject("os.path", "exists")and up until
existsis used, the import is not triggered
- here "used" means "access attributes or call it"
- so, CW uses (My)LazyObject to prepare to import a bunch of cubes (but not importing them) and mark them as callable_deprecated
- this allow to raise a deprecation warning if they are used but not add those cubes as dependencies in CW
Now, why is it a problem? Well ...
- turns out:
functools.@wrapsactually try to access a bunch of attributes on the wrapped callable to grab
, '__doc__to avoid breaking a bunch of stuff
- going back to before, you might have guessed it: by trying to access those attributes,
functools.@wrapstriggers the import of those objects
- which in turns breaks everything because this is supposed to be a lazy import and so it was supposed to handle the case where those dependencies weren't installed
- and you get an ImportError and your code doesn't work anymore
So, this MR:
- recreate the behavior of functools.wraps but in a lazy way to solve this issue
- it also ensure that we never access attributes of the decorated callable before this decorated callable is actually called
- document LazyObject because pffff
I think it's the first time I had to debug one exception that triggered 3-4 other exceptions ^^'
Also protip: uses pdbpp instead of pdb, it allows to access hidden frames.