ty icon indicating copy to clipboard operation
ty copied to clipboard

Possibly missing import

Open ion-elgreco opened this issue 8 months ago • 6 comments

Summary

Ty is throwing a possible unbound import, but pyright doesn't do this.

I have a module package.custom that does this, at runtime it adds a certain class to a module or not, based on the required dependencies. If you don't have the dependency installed, it will not show it on the main module, when someone tries to import directly from package.mod.custom it will throw an import error.

try:
    from package.mod.custom import CustomManager

    __all__.extend(["CustomManager"])
except ImportError as e:
    if "pydantic" in str(e):
        pass
    else:
        raise e

In package.mod.custom we do this:

try:
    import pydantic
except ImportError as e:
    if "pydantic" in str(e):
        raise ImportError(
            "Install 'package[pydantic]' to use pydantic functionality",
        ) from e
    else:
        raise e

Version

ty 0.0.0-alpha.8 (0474b40e1 2025-05-09)

ion-elgreco avatar May 10 '25 16:05 ion-elgreco

Thank you for reporting this.

I can't directly try your example because I don't have the full code and information about the project structure, but if I understand correctly, you basically have a setup similar to this:

main.py:

from pydantic_support import CustomPydanticFeature

# Use the feature:
c = CustomPydanticFeature()

pydantic_support.py:

try:
    import pydantic

    class CustomPydanticFeature: ...

except ImportError:
    print("Please install pydantic if you want to use this feature")

In general, we can not model runtime behavior like whether or not the pydantic import leads to an ImportError or not. This seems like a case for a suppression comment for me? You could add a # ty: ignore[possibly-unbound-import] comment in the first line there or generally suppress possibly-unbound-import diagnostics in your project's configuration.

We realize that this is a common pattern, however, so we're also happy to take more feedback and opinions here.

sharkdp avatar May 13 '25 10:05 sharkdp

Wouldn't resolving to pydantic tell you that you that CustomPydanticFeature will be available?

The issue is that this is done in external libraries as well, so when I would import things from external libraries, you would get these errors reported from ty, for example dagster-polars: https://github.com/dagster-io/community-integrations/blob/main/libraries/dagster-polars/dagster_polars/init.py

ion-elgreco avatar May 13 '25 10:05 ion-elgreco

Wouldn't resolving to pydantic tell you that you that CustomPydanticFeature will be available?

For the specific case of a try: import …; except ImportError: …, we might theoretically be able to detect that somehow and check whether the import would resolve or not. For a user of such a feature (main.py), I can definitely see how that would be a desirable outcome. Instead of "possibly unbound", the CustomPydanticFeature would either be definitively bound or definitely unbound.

For the author of a package that includes this pydantic_support module, things are maybe a bit more complex? Would they need to run their type checking twice, once with that optional dependency installed, and once without? Or would it be more useful to have the CustomPydanicFeature "possibly unbound", the way we treat it right now? That way, even if you don't have pydantic installed, you could still get proper type checking of code that makes use of CustomPydanticFeature.

sharkdp avatar May 13 '25 11:05 sharkdp

Wouldn't resolving to pydantic tell you that you that CustomPydanticFeature will be available?

For the specific case of a try: import …; except ImportError: …, we might theoretically be able to detect that somehow and check whether the import would resolve or not. For a user of such a feature (main.py), I can definitely see how that would be a desirable outcome. Instead of "possibly unbound", the CustomPydanticFeature would either be definitively bound or definitely unbound.

I agree!

For the author of a package that includes this pydantic_support module, things are maybe a bit more complex? Would they need to run their type checking twice, once with that optional dependency installed, and once without? Or would it be more useful to have the CustomPydanicFeature "possibly unbound", the way we treat it right now? That way, even if you don't have pydantic installed, you could still get proper type checking of code that makes use of CustomPydanticFeature.

Usually you would run tests with and without a dependency installed, it's something we do in delta-rs with pandas dependency.

ion-elgreco avatar May 13 '25 14:05 ion-elgreco

I get this error when using opensearchpy module:

# .venv/lib/python3.13/site-packages/opensearchpy/__init__.py

...

try:
    from ._async.client import AsyncOpenSearch
    from ._async.http_aiohttp import AIOHttpConnection, AsyncConnection
    from ._async.transport import AsyncTransport
    from .connection import AsyncHttpConnection
    from .helpers import AWSV4SignerAsyncAuth

    __all__ += [
        "AIOHttpConnection",
        "AsyncConnection",
        "AsyncTransport",
        "AsyncOpenSearch",
        "AsyncHttpConnection",
        "AWSV4SignerAsyncAuth",
    ]
except (ImportError, SyntaxError):
    pass

And when I do

from opensearchpy import AsyncOpenSearch

ty complains:

warning[possibly-unbound-import]: Member `AsyncOpenSearch` of module `opensearchpy` is possibly unbound
  --> backend/settings.py:58:34
58 |         from opensearchpy import AsyncOpenSearch
   |                                  ^^^^^^^^^^^^^^^
info: rule `possibly-unbound-import` is enabled by default

merlinz01 avatar Sep 22 '25 18:09 merlinz01

I think that unless we can understand such cases better, we should probably silence possibly-unbound-import by default as too noisy with false positives.

carljm avatar Nov 14 '25 14:11 carljm