pylance-release icon indicating copy to clipboard operation
pylance-release copied to clipboard

Local import inside conda editable package doesn't work.

Open elashrry opened this issue 3 years ago • 1 comments

Environment data

  • Language Server version: 2022.9.21
  • OS and version: darwin arm64
  • Python version (and distribution if applicable, e.g. Anaconda): Python3.9 via anaconda
  • python.analysis.indexing: null
  • python.analysis.typeCheckingMode: off

Repro Steps

  1. open new working space test in VSCode and have the following folders structure:
test/
    folder1/
        module.py
        test.py
        test.ipynb
    folder2/
        subfolder/
            module.py
            test.py
            test.ipynb

The contents of folder1/module.py and folder2/subfolder/module.py are:

def func():
    """
    This is a function
    """
    print("Hello World")

and all other test.py and test.ipynb have one line

from module import func
  1. create a conda environment with Python3.9 (or any other version)
  2. activate it and use it as python interpreter for the working space.
  3. verify all local imports are resolved correctly by Pylance.
  4. add folder2 as an editable package via conda develope ./folder2 from the root directory of the working space.
  5. Now local import inside folder2/subfolder does not work.

Expected behavior

Local import inside conda editable package work normally.

Actual behavior

once you add a folder to conda using the command conda develop directory/path, the local import breaks inside this package.

Note

If I use explicit relative import, Pylance works normally inside `folder2/subfolder` but python will not work with explicit relative import in the running scripts or notebooks.

Logs

Pylance_logs.txt

elashrry avatar Sep 20 '22 09:09 elashrry

Experiencing a similar issue on Podman. Very annoying.

Jacques-Peeters avatar Sep 20 '22 11:09 Jacques-Peeters

Here's a repro that does not involve conda:

<workspace-root>\folder2\subfolder\module.py:

def func():
    """
    This is a function
    """
    print("Hello World")

<workspace-root>\folder2\subfolder\test.py:

from module import func

The import above works fine until you add the following as a .pth file in the site-packages directory, which is what the conda develop ./folder2 command does:

<absolute-path-to-workspace-root>\folder2

Once the .pth file is present, ParentDirectoryCache.checkValidPath will return false. There's a comment there that says:

if (this._libPathCache.some((p) => sourceFilePath.startsWith(p))) {
    // Make sure it is not lib folders under user code root.
    // ex) .venv folder
    return false;
}

And now the import is flagged as an error: Import "module" could not be resolved

@heejaechang, @erictraut, it's not clear to me if this is a bug or by design. Having a .pth file that points to a location within the workspace seems a little weird?

debonte avatar Oct 07 '22 20:10 debonte

Btw, I also noticed that if the .pth points to <absolute-path-to-workspace-root>\folder2\subfolder, then the import works. It seems strange to me that the .pth pointing to folder2 causes an error but pointing to folder2\subfolder does not.

debonte avatar Oct 07 '22 20:10 debonte

@jakebailey added the code that implements the heuristics for module import resolution within the local project. Maybe he remembers why he added that condition.

erictraut avatar Oct 08 '22 00:10 erictraut

If this is the "go up from the current module" heuristic, I didn't implement it; I did push the code out to pyright, though, if you're going off of the pyright blame. HeeJae would know better, though I think it's very possible we just didn't know this would interact with pth files in any way.

pth files to locations in the workspace root should be fine, since that's how editable installs work (well, ignoring that new PEP).

jakebailey avatar Oct 09 '22 16:10 jakebailey

pth files to locations in the workspace root should be fine, since that's how editable installs work (well, ignoring that new PEP).

Oh, I was thinking that most editable install scenarios would be pointing to files in some other repo/enlistment, and that was why the pth was needed -- so we could actually find those files.

When an editable install's source files are within the workspace, does the presence of the pth enable us to generate/understand correct import paths? Package-relative paths instead of directory-relative paths?

debonte avatar Oct 09 '22 16:10 debonte

It can go both ways; I've seen some projects do this, especially those with a complicated build.

Theoretically it should work, because pth files just tack paths onto sys.path, so are just another import root. But, I would think that if you have a package installed via an editable install that you wouldn't want any heuristic for that package at that point, as the editable install is simulating a real install and the paths better be correct at that point...

In any case, I haven't put myself in the headspace to read over this whole thing and remember what's going on, so forgive me if I'm adding confusion. It's been a while!

jakebailey avatar Oct 09 '22 18:10 jakebailey

If I use explicit relative import, Pylance works normally inside folder2/subfolder but python will not work with explicit relative import in the running scripts or notebooks.

@elashrry, sorry for the long delay on this. I started looking at this again today and my impression both from Pylance's behavior and runtime behavior is that using a relative import (from .module import func) in <workspace-root>\folder2\subfolder\test.py would be correct.

Can you provide more details on the problems that you see at runtime when using an explicit relative import?

debonte avatar Apr 07 '23 23:04 debonte

Hello @debonte, thank you for coming back on it.

Explicit relative import as you mentioned would work with Pylance, but if you are running test.py as a script (not as a module), Python will give the error

ImportError: attempted relative import with no known parent package

elashrry avatar Apr 08 '23 18:04 elashrry

Ok. Here are two approaches that may solve this for you:

  1. Keep the absolute import (from module import func) and create a pyrightconfig.json file that specifies test\folder2\subfolder as an execution environment root directory. There's documentation on this here and some background details here.
{
  "executionEnvironments": [
    {
      "root": "test/folder2/subfolder"
    }
  ]
}
  1. Use a relative import (from .module import func) and run test.py via python -m. For example, from the test directory run python -m folder2.subfolder.test.

debonte avatar Apr 10 '23 23:04 debonte

Thank you @debonte for the workaround. I want to add that adding

    "python.analysis.extraPaths": [
        "folder2/sub_folder" 
    ]

to the .vscode/settings.json file will work too.

elashrry avatar Apr 15 '23 18:04 elashrry