isinstance fails for a type imported via package and from the same module directly

You should not both make lib a package and add it to PYTHONPATH. This makes it possible to import its modules both as lib. and directly, setting yourself up for failure.

As you can see,

lib.Types.Custom != Types.Custom

because of the way Python imports work.

Python searches the import path and parses an appropriate entry that it finds.

  • When you import lib.Types, it imports the lib directory as a package, then lib/Types.py as a submodule inside it, creating module objects lib and lib.Types in sys.modules.
  • When you import Types, it imports Types.py as a standalone module, creating a module object Types in sys.modules.

So, Types and lib.Types end up as two different module objects. Python doesn't check if they are the same file to keep things simple and to avoid second-guessing you.

(This is actually listed in the Traps for the Unwary in Python’s Import System article as the "double import trap".)


If you remove lib from PYTHONPATH, the import in lib/foo.py would need to become a relative import:

from .Types import Custom

or an absolute import:

from lib.Types import Custom

When a module is imported thru two different path in the same process - like here with import Types in foo.py and import lib.Types in main.py, it is really imported twice, yielding two distinct module objects, each with it's own distinct functions and class instances (you can check by yourself using id(obj_or_class)), effectively breaking is and isinstance tests.

The solution here would be to add Project (not Project/lib) to your pythonpath (fwiw that's what should have been done anyway - pythonpath/sys.path should be a list of directories containing packages and modules, not the packages directories themselves) and use from lib.Type import Custom everywhere, so you only have one single instance of the module.