Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pylance v2024.10.1 doesn't work with Django models #6562

Closed
luanft opened this issue Oct 16, 2024 · 10 comments
Closed

Pylance v2024.10.1 doesn't work with Django models #6562

luanft opened this issue Oct 16, 2024 · 10 comments
Assignees
Labels
user responded Was "waiting for user response" and they responded

Comments

@luanft
Copy link

luanft commented Oct 16, 2024

The Pylance version v2024.8.1 works well on django models. After I updated the Pylance to latest version v2024.10.1. Pylance was failed to get the id, foreign key properties. Can you help to check it?

Environment data

  • Pylance version: v2024.10.1
  • OS and version: Centos 7
  • Python version (& distribution if applicable, e.g. Anaconda): 3.10.6 (main, Jun 19 2024, 14:45:38) [GCC 7.3.0] on linux
  • Django version: 4.2
  • django-stubs: 4.2.7

Code Snippet

    def get_referred_by_projects(self):
        """Return the jsom format for the refering obj"""
        return {
            "id": self.id,
            "type": self.get_reversed_type(),
            'design': self.design.name,
            'design_id': self.design.id,
            'design_type': self.design.get_design_type(),
            "use_version": "",
            "latest_version": "",
            "since_version": "",
            "latest_design": "",
            "date": user_tz(self.created_date),
            "added_by": self.added_by.get_full_idname(),
            'obj_name' : ''
        }

Pyright config

{
    "exclude": [
        "db_conversion/**",
        "ipds/templates/**",
        "ipds/static/**"
    ],
    "include": [
        "ipds/**",
        "ipds/**/**"
    ]
}

Issue

Image

Logs

2024-10-16 16:36:24.665 [info] (Client) Pylance async client (2024.10.1) started with python extension (2024.14.1)
2024-10-16 16:36:26.783 [info] [Info  - 4:36:26 PM] (24516) Server root directory: file:///home/users/luant/.vscode-server/extensions/ms-python.vscode-pylance-2024.10.1/dist
2024-10-16 16:36:26.945 [info] [Info  - 4:36:26 PM] (24516) Pylance language server 2024.10.1 (pyright version 1.1.382, commit e46efc6b) starting
2024-10-16 16:36:26.971 [info] [Info  - 4:36:26 PM] (24516) Starting service instance "django-ipds"
2024-10-16 16:36:27.040 [info] [Info  - 4:36:27 PM] (24516) Starting service instance "ipkits"
2024-10-16 16:36:27.052 [info] [Info  - 4:36:27 PM] (24516) Starting service instance "commons"
2024-10-16 16:36:28.006 [info] [Info  - 4:36:28 PM] (24516) Loading configuration file at /home/users/luant/proj/django-ipds/pyrightconfig.json
2024-10-16 16:36:28.014 [info] [Info  - 4:36:28 PM] (24516) Setting pythonPath for service "django-ipds": "/home/users/luant/SVR17_ADM_NEW/bin/python"
2024-10-16 16:36:28.169 [info] [Info  - 4:36:28 PM] (24516) Assuming Python version 3.10.6.final.0
2024-10-16 16:36:34.552 [info] [Info  - 4:36:34 PM] (24516) Found 254 source files
2024-10-16 16:36:34.587 [info] [Info  - 4:36:34 PM] (24516) Background analysis(1) root directory: file:///home/users/luant/.vscode-server/extensions/ms-python.vscode-pylance-2024.10.1/dist
2024-10-16 16:36:34.587 [info] [Info  - 4:36:34 PM] (24516) Background analysis(1) started
2024-10-16 16:36:34.589 [info] [Info  - 4:36:34 PM] (24516) Setting environmentName for service "ipkits": "3.10.6 (SVR17_ADM_NEW venv)"
2024-10-16 16:36:34.589 [info] [Info  - 4:36:34 PM] (24516) Setting pythonPath for service "ipkits": "/home/users/luant/SVR17_ADM_NEW/bin/python"
2024-10-16 16:36:34.589 [info] [Info  - 4:36:34 PM] (24516) No include entries specified; assuming /home/users/luant/proj/ipkits
2024-10-16 16:36:34.753 [info] [Info  - 4:36:34 PM] (24516) Assuming Python version 3.10.6.final.0
2024-10-16 16:36:38.410 [info] [Info  - 4:36:38 PM] (24516) Found 189 source files
2024-10-16 16:36:38.417 [info] [Info  - 4:36:38 PM] (24516) Background analysis(2) root directory: file:///home/users/luant/.vscode-server/extensions/ms-python.vscode-pylance-2024.10.1/dist
2024-10-16 16:36:38.417 [info] [Info  - 4:36:38 PM] (24516) Background analysis(2) started
2024-10-16 16:36:38.418 [info] [Info  - 4:36:38 PM] (24516) Setting environmentName for service "commons": "3.10.6 (SVR17_ADM_NEW venv)"
2024-10-16 16:36:38.418 [info] [Info  - 4:36:38 PM] (24516) Setting pythonPath for service "commons": "/home/users/luant/SVR17_ADM_NEW/bin/python"
2024-10-16 16:36:38.418 [info] [Info  - 4:36:38 PM] (24516) No include entries specified; assuming /home/users/luant/proj/commons
2024-10-16 16:36:38.542 [info] [Info  - 4:36:38 PM] (24516) Assuming Python version 3.10.6.final.0
2024-10-16 16:36:41.288 [info] [Info  - 4:36:41 PM] (24516) Found 109 source files
2024-10-16 16:36:42.764 [info] [Info  - 4:36:42 PM] (24516) Background analysis(3) root directory: file:///home/users/luant/.vscode-server/extensions/ms-python.vscode-pylance-2024.10.1/dist
2024-10-16 16:36:42.764 [info] [Info  - 4:36:42 PM] (24516) Background analysis(3) started
2024-10-16 16:36:42.764 [info] [Info  - 4:36:42 PM] (24516) Indexer background runner(4) root directory: file:///home/users/luant/.vscode-server/extensions/ms-python.vscode-pylance-2024.10.1/dist (index)
2024-10-16 16:36:42.764 [info] [Info  - 4:36:42 PM] (24516) Indexing(4) started
2024-10-16 16:36:45.771 [info] [Info  - 4:36:45 PM] (24516) [BG(1)] Long operation: SemanticTokens range 2052:0 - 2139:74 at file:///home/users/luant/proj/django-ipds/ipds/models/design/base.py (3058ms)
2024-10-16 16:36:52.725 [info] [Info  - 4:36:52 PM] (24516) [BG(1)] Long operation: SemanticTokens full at file:///home/users/luant/proj/django-ipds/ipds/models/design/base.py (6952ms)
2024-10-16 16:36:57.364 [info] [Info  - 4:36:57 PM] (24516) [IDX(4)] Long operation: scan packages file:///home/users/luant/proj/django-ipds (14709ms)
2024-10-16 16:36:57.372 [info] [Info  - 4:36:57 PM] (24516) scanned(4) 173 files over 1 exec env
2024-10-16 16:37:03.423 [info] [Info  - 4:37:03 PM] (24516) [FG] Long operation: parsing: file:///home/users/luant/proj/django-ipds/ipds/util/main.py (2151ms)
2024-10-16 16:37:04.483 [info] [Info  - 4:37:04 PM] (24516) [IDX(4)] Long operation: parsing: file:///home/users/luant/.vscode-server/extensions/ms-python.vscode-pylance-2024.10.1/dist/bundled/stubs/networkx/classes/ordered.pyi (2203ms)
2024-10-16 16:37:04.483 [info] [Info  - 4:37:04 PM] (24516) [IDX(4)] Long operation: binding: file:///home/users/luant/.vscode-server/extensions/ms-python.vscode-pylance-2024.10.1/dist/bundled/stubs/networkx/classes/__init__.pyi (2207ms)
2024-10-16 16:37:04.483 [info] [Info  - 4:37:04 PM] (24516) [IDX(4)] Long operation: binding: file:///home/users/luant/.vscode-server/extensions/ms-python.vscode-pylance-2024.10.1/dist/typeshed-fallback/stubs/networkx/networkx/__init__.pyi (2380ms)
2024-10-16 16:37:04.520 [info] [Info  - 4:37:04 PM] (24516) [IDX(4)] Long operation: indexing: file:///home/users/luant/.vscode-server/extensions/ms-python.vscode-pylance-2024.10.1/dist/typeshed-fallback/stubs/networkx/networkx/__init__.pyi (4365ms)
2024-10-16 16:37:12.824 [info] [Info  - 4:37:12 PM] (24516) [IDX(4)] Long operation: parsing: file:///home/users/luant/SVR17_ADM_NEW/lib/python3.10/site-packages/jwt/jwks_client.py (2485ms)
2024-10-16 16:37:12.824 [info] [Info  - 4:37:12 PM] (24516) [IDX(4)] Long operation: indexing: file:///home/users/luant/SVR17_ADM_NEW/lib/python3.10/site-packages/jwt/__init__.py (2546ms)
2024-10-16 16:37:15.176 [info] [Info  - 4:37:15 PM] (24516) [IDX(4)] Long operation: index execution environment file:///home/users/luant/proj/django-ipds (17157ms)
2024-10-16 16:37:18.448 [info] [Info  - 4:37:18 PM] (24516) [IDX(4)] Long operation: index packages file:///home/users/luant/proj/django-ipds (20436ms)
2024-10-16 16:37:18.456 [info] [Info  - 4:37:18 PM] (24516) indexed(4) 173 files over 1 exec env
2024-10-16 16:37:18.721 [info] [Info  - 4:37:18 PM] (24516) Indexing finished(4).

@github-actions github-actions bot added the needs repro Issue has not been reproduced yet label Oct 16, 2024
@StellaHuang95
Copy link
Contributor

Thanks for reaching out. To help us investigate, could you provide the complete code sample that reproduces the issue? If your project is in a public repo, sharing the link would also be helpful.

Could you also provide more logs as outlined in our troubleshooting guide here? The logs you shared don't seem to have python.analysis.logLevel set to Trace.

Without the full project, it's hard to diagnose the exact problem. However, if you're seeing new diagnostics, it might be related to the typeCheckingMode setting. Could you check if you have typeCheckingMode enabled in your settings.json or config file? This might explain the change.

@StellaHuang95 StellaHuang95 added waiting for user response Requires more information from user and removed needs repro Issue has not been reproduced yet labels Oct 16, 2024
@debonte
Copy link
Contributor

debonte commented Oct 16, 2024

This is a known issue. These fields are added dynamically by django and there's currently no way in the Python type system to tell a static type checker that they will exist.

General improvements for django are tracked by #3701

@luanft
Copy link
Author

luanft commented Oct 17, 2024

@StellaHuang95 Please see my attached sample script and the log. server_log.txt

Pylance was failed to check field id from django model and some fields from django rest framework such data, query_params. Pylance version v2024.8.1 can check these fields.

from django.db import models
from rest_framework.response import Response
from rest_framework import viewsets, serializers, permissions, decorators

class Author(models.Model):
    """Author model"""
    name = models.CharField(max_length=100, help_text="Author name")
    description = models.TextField(max_length=200, help_text="Author description")
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        """To string"""
        return self.name


    def get_books(self):
        """Get books"""
        return Book.objects.filter(author_id=self.id)

    def update_signature(self, signature=None):
        """Update signature"""
        if signature is None:
            raise ValueError("Signature is required")

        for obj in self.get_books():
            obj.signature = signature
            obj.save()


class Signature(models.Model):
    """Signature model"""
    name = models.CharField(max_length=100, help_text="Signature name")
    data = models.TextField(max_length=200, help_text="Signature description")
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        """To string"""
        return self.name

class Book(models.Model):
    """Test model"""
    title = models.CharField(max_length=100)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    description = models.TextField(max_length=100)
    signature = models.TextField(max_length=100)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        """To string"""
        return self.title


    def get_author_name(self):
        """Get author name"""
        return self.author.name


    def set_author(self, author_obj:'Author'):
        """Set author"""
        if author_obj is None:
            raise ValueError("Author is required")

        if author_obj.get_books().count() > 5:
            raise ValueError("Author has too many books")

        self.author = author_obj
        self.save()

    def set_signutare(self):
        """Set signature"""
        signature_obj = Signature.objects.filter(author_id=self.author.id).first()
        if signature_obj is None:
            raise ValueError("Signature not found")

        self.signature = signature_obj
        self.save()


class AuthorSerializer(serializers.ModelSerializer):
    """Author serializer"""
    class Meta:
        model = Author
        fields = '__all__'

class AuthorViewSet(viewsets.ModelViewSet):
    """Author view set"""
    queryset = Author.objects.all()
    serializer_class = AuthorSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]


    @decorators.action(
        detail=False, methods=['get'],
        url_path='find-author', url_name='find-author')
    def find_author(self, *args, **kwargs):
        """Get author name"""
        name = self.request.query_params.get('name', '')
        return Response({ "data": name })


    @decorators.action(
        detail=True, methods=['post'],
        url_path='create-signature', url_name='create-signature')
    def create_signature(self, *args, **kwargs):
        """Get author name"""
        print(self.request.data)
        return Response({ "data": "created" })

This is the error from Pylance

Image

@github-actions github-actions bot added user responded Was "waiting for user response" and they responded and removed waiting for user response Requires more information from user labels Oct 17, 2024
@luanft
Copy link
Author

luanft commented Oct 17, 2024

This is a known issue. These fields are added dynamically by django and there's currently no way in the Python type system to tell a static type checker that they will exist.

General improvements for django are tracked by #3701

@debonte In version v2024.8.1. I didn't face this issue. Please see my screenshot for version 2024.8.1
Image

@vsoraas
Copy link

vsoraas commented Oct 17, 2024

The same issue rears its head elsewhere when using dynamic model fetching, in this example with djangorestframework's get_object().

Image

As a result, any type inferences for children and children's children of that object (in this instance, company) get mangled into NoReturn too, and no intellisense.

In pylance==2024.9.2, everything works as expected:

Image

As far as I can tell, this particular invocation of the issue arises in djangorestframework's GenericAPIView.get_object().

In pylance==2024.9.2, this function is evaluated to return Any.

In pylance==2024.10.1, the function is evaluated to return NoReturn - despite the fact that obj is Any during the return statement (for clarity, Django's get_object_or_404() is evaluated to return Any):

Image

@rchiodo
Copy link
Contributor

rchiodo commented Oct 17, 2024

NoReturn generally means Pylance/Pyright thinks the function is throwing an exception. Looking at that code, maybe that's a bug in Pyright? It must think the assert always fires?

@StellaHuang95
Copy link
Contributor

StellaHuang95 commented Oct 17, 2024

@luanft, from the logs, I see Loading configuration file at /home/users/luant/proj/django-ipds/pyrightconfig.json, which indicates that you have a configuration file in your workspace. In this case, there's a recent change we make such that the config file takes precedence, and certain settings, including typeCheckingMode, will be applied regardless of what's set in settings.json.

What's causing confusion is probably that the default value for typeCheckingMode in a pyrightconfig.json file is standard if not explicitly specified, whereas the default in settings.json is off. So, when the pyrightconfig file is present, the standard mode is applied to your workspace, enabling multiple diagnostic rules.

I think we could improve clarity by adding more detail to the warning message when typeCheckingMode is overridden by the configuration file

@luanft
Copy link
Author

luanft commented Oct 18, 2024

@luanft, from the logs, I see Loading configuration file at /home/users/luant/proj/django-ipds/pyrightconfig.json, which indicates that you have a configuration file in your workspace. In this case, there's a recent change we make such that the config file takes precedence, and certain settings, including typeCheckingMode, will be applied regardless of what's set in settings.json.

What's causing confusion is probably that the default value for typeCheckingMode in a pyrightconfig.json file is standard if not explicitly specified, whereas the default in settings.json is off. So, when the pyrightconfig file is present, the standard mode is applied to your workspace, enabling multiple diagnostic rules.

I think we could improve clarity by adding more detail to the warning message when typeCheckingMode is overridden by the configuration file

@StellaHuang95
This is my pyright configuration file. I didn't define the typeCheckingMode in the pyright configuration file

{
    "exclude": [
        "db_conversion/**",
        "ipds/templates/**",
        "ipds/static/**"
    ],
    "include": [
        "ipds/**",
        "ipds/**/**"
    ]
}

@StellaHuang95
Copy link
Contributor

@luanft Right, so two things are happening here:

  1. When a pyrightconfig.json file is present, any settings that can be specified in the config file will override those in settings.json.

  2. If typeCheckingMode is not explicitly set in pyrightconfig.json, the default value standard takes precedence over the off value in settings.json.

I hope this clears up the confusion. To get the same behavior, you can add "typeCheckingMode": "off" to your pyrightconfig.json

@luanft
Copy link
Author

luanft commented Oct 21, 2024

@StellaHuang95 Thank you, define typeCheckingMode = off in pyright config worked for me.

@luanft luanft closed this as completed Oct 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
user responded Was "waiting for user response" and they responded
Projects
None yet
Development

No branches or pull requests

5 participants