1

I have a class which looks like this:

class Foo:
    
  def __init__(self, string: str) -> None:
    self._string = string
    
  def __add__(self, other: str) -> str:
    if not isinstance(other, str):
      return NotImplemented
        
    return f'{self._string}foo{other}'

Mypy says the 8th line (return NotImplemented) is unreachable, which is reasonable since I already type-hinted other as a str. However, at runtime, other might not be a string, and if that's the case I would like to return NotImplemented so that Python would raise an exception unless other can handle the operation.

Is there a way, other than turning off --warn-unreachable and use a comment, to nicely let mypy know that no one would ever hear its rambling at runtime and that I need an explicit check?

InSync
  • 4,851
  • 4
  • 8
  • 30
  • Does this answer your question? [How can mypy ignore a single line in a source file?](https://stackoverflow.com/questions/49220022/how-can-mypy-ignore-a-single-line-in-a-source-file) – Brian61354270 Aug 18 '23 at 14:00
  • @Brian61354270 No, it doesn't. As stated, I would like to avoid such comments. – InSync Aug 18 '23 at 14:08
  • 1
    Could you clarify why that is? It's not clear why you simultaneously want to disable a lint for a specific line and also don't want to use mypy's feature for doing so. – Brian61354270 Aug 18 '23 at 14:24
  • @Brian61354270 To be exact, I want it to accept a block of code as "might be reachable" at runtime. As a matter of taste I prefer not to have comments in my code, especially "tool configuration" comments, so I want a better way. – InSync Aug 18 '23 at 14:34
  • 3
    Your approach is extremely un-pythonic IMHO. You already offer the type annotation contract to users of your class, i.e. _"here is my `__add__` method, as you can see it expects `other` to be a `str`"_. Everybody can see that. If they choose to pass something else as an argument, **that is on them**. Neither you nor they gain anything meaningful from that explicit runtime type check. Not to mention that function will work just fine with just about any type passed to it. Why do you feel the need to **forbid** people from using it as they see fit? – Daniil Fajnberg Aug 18 '23 at 18:51
  • 2
    This is something I see fairly often with people that are new to Python. They mean well and want to "cover their bases" by explicitly checking for argument types to be as expected. But that goes against the entire philosophy of Python. Let people (mis-)use your functions, how they want. Let them deal with the errors, if they ignored your hints/suggestions. They might even find cool ways that you did not think of, when you designed it. There are exceptions to this rule of course, but your example is not one of them. – Daniil Fajnberg Aug 18 '23 at 18:58
  • @DaniilFajnberg Fair enough. Thanks for the suggestion. – InSync Aug 18 '23 at 19:16
  • 1
    Just for the record, I didn't want to allow additions of anything other than `str`, especially two objects of this same type, since there's no way to determine some other factors irrelevant here. I was trying to raise an explicit error in this case ("*Errors should never pass silently*"), but it seems that I was not doing things correctly/Pythonically. – InSync Aug 18 '23 at 19:25
  • @DaniilFajnberg unlike most other cases, I'd argue that for `__add__` and other binary and inplace methods (`__eq__`, `__iadd__`) this check makes sense. Returning `NotImplemented` is quite important here. Namely, if I declare `class Bar` and want to make it addable to `Foo`, `Foo.__add__` must not raise an error in such case, and "it expects other to be a `str`" is plain wrong when I call `Foo() + Bar()`, which involves other methods besides `Foo.__add__`. Consider [this](https://mypy-play.net/?mypy=master&python=3.10&flags=strict%2Cwarn-unreachable&gist=183a982397e3be627aaf25aa26aa0197). – STerliakov Aug 19 '23 at 12:43
  • Finally, you shouldn't broaden `__add__` signature (if you do `def __add__(self, other: object) -> str`, you'll get incorrect type `str` for `Foo() + Bar()` from my example above). I'd really say that this is perfect as is with comment, but if you feel the need to improve the handling - consider submitting an issue at `mypy` [issue tracker](https://github.com/python/mypy/issues). This can be an interesting feature request: avoid marking branches in dunder operator methods that return `NotImplemented` after `other` type narrowing. – STerliakov Aug 19 '23 at 12:56
  • *"Let people (mis-)use your functions, how they want"* describes python type system very nicely, and I wish this comment was on some other post where type strictness is enforced "just to be ok". In this case, such `__add__` "misusing" suggests implementing `__radd__` on another type to me, so not making this troublesome can be a way to improve usability. – STerliakov Aug 19 '23 at 13:04
  • 1
    @InSync re: "I would like to avoid such comments", it may interest you to know that those comments aren't ad-hoc convention by mypy, but a fundamental part of the type hinting feature. `# type: ` comments are specified in [PEP 484 – Type Hints](https://peps.python.org/pep-0484/) and [PEP 526 – Syntax for Variable Annotations](https://peps.python.org/pep-0526/), and are also [included in Python's abstract syntax tree](https://docs.python.org/3/library/ast.html#abstract-grammar) – Brian61354270 Aug 23 '23 at 21:11
  • @Brian61354270 Actually, no. It doesn't matter to me whether such comments are a part of Python, mypy or whatever; I just don't like them. – InSync Aug 23 '23 at 22:54

0 Answers0