That subproblem is fundamentally unfixable unless you're willing to allow incompatible objects to be passed around, which is a really bad idea.
If your code does:
o = foo_v1.get_obj()
foo_v2.use_obj(o)
then this is almost always undefined behavior, since the authors of `foo` will almost certainly not write their APIs to accept instances with different internals.
With a more advanced system you can look at whether a package's use of foo is exposed in its API or completely internal, and then allow different isolated islands of packages to use different versions of foo.