python / python/mypy

Assignment to Final attribute incorrectly flagged in conditional assignment within __init__

Ouverte
#20,443 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

bug topic-final
Langage dominant
Python
Étoiles
20.6k
Forks
3.3k
Métriques de merge des PR
Métriques de PR en attente

Description

Bug Report

Documentation on typing.Final says (emphasis mine):

Final names cannot be reassigned in any scope. Final names declared in class scopes cannot be overridden in subclasses.

The problem comes in a class's init, if the final member is conditionally assigned to different values (e.g. assignments in an if/else expression, if the assignment to the member occurs in both blocks). In the if/else case, mypy flags the assignment in the else block.

To Reproduce

from typing import Final


class Foo:
    attr: Final[str]

    def __init__(self, flag: bool) -> None:
        if flag:
            self.attr = "bar"
        else:
            self.attr = "baz"  # mypy flags with:  error: Cannot assign to final attribute "attr"  [misc]

Expected Behavior

I would expect mypy to not flag this code at all. For either possible value of flag, there is only a single assignment, so no reassignment occurs.

Actual Behavior

Mypy flags line 11 with: error: Cannot assign to final attribute "attr" [misc]

Your Environment

  • Mypy version used: mypy 1.19.1 (compiled: yes)
  • Mypy command-line flags: none; just the path to the repro test script
  • Mypy configuration options from mypy.ini (and other config files):
[tool.mypy]
python_version = "3.13"

# set mypy_path and explicit_package_bases, otherwise mypy gets confused about package names
# due to the use of namespace packages.
mypy_path = "src"
explicit_package_bases = true

pretty = true
disallow_any_unimported = true
disallow_any_expr = false
disallow_any_explicit = false
disallow_any_generics = true
disallow_subclassing_any = true
disallow_untyped_calls = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
disallow_untyped_decorators = true
implicit_optional = true
strict_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_return_any = true
warn_unreachable = true
warn_unused_configs = true
exclude = ["dist/.*/.*\\.py$"]
  • Python version used: 3.13.7

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez par le reproducteur Python fourni et exécutez-le avec mypy 1.19.1 pour confirmer le diagnostic sur l’affectation de la branche else. Suivez le traitement des attributs Final lors de l’affectation conditionnelle et ajoutez une couverture de régression pour l’exemple ; la tâche est terminée lorsque les deux branches sont vérifiées par le système de types sans l’erreur de réaffectation.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
devtools
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
À l'abandon
Clarté
Clairement spécifiée
Accessibilité débutants
48/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.