Fix usage of callable_deprecation on LazyObject that was breaking CW 3.24 (or something like that)

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 @wraps decorator 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 exists is 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.@wraps actually try to access a bunch of attributes on the wrapped callable to grab __name__, 'dict, '__doc__ to avoid breaking a bunch of stuff
  • going back to before, you might have guessed it: by trying to access those attributes, functools.@wraps triggers 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.

Merge request reports