I've wondered this exact same thing myself for other languages. I doubt its a lack of familiarity since I would bet most compiler/language authors are aware of most Haskell concepts/idioms. I think it probably has more to do with function definition semantics in non-functional languages but it seems that could be easily worked around...
Anyway the haskell way, especially with Typeclasses, has to be clearest way to express type constraints for functions I've encountered at least from a code readability standpoint.
I love Haskell, but I'm not sure this would actually be better for Python. To be Haskell-like, the comment would actually be different than what you wrote:
#greeting :: str -> int -> str
and this is specifically related to the functional nature of Haskell. `greeting "hello"` now instantly becomes a function with type :: int -> str. So the Haskell syntax is about currying, it's about how the function you wrote is really just a cascade of functions.
Doing something silly like making the "arguments" reside in an artificial tuple would definitely be bad practice in most Haskell cases, and so syntactically looks a bit weird and out of place, like it's shoehorned on just because Python function arguments are not like Haskell arguments.
Since Python is not based on this model of a function, I think it's a lot better to keep the syntax in the actual def statement. Adding the return type annotation at the end is nice.
Most probably, this stuff was added to the signature because there were already natural tools like the inspect library that would provide the immediate hooks for making use of it. But even if the implementation didn't need it to be part of the signature like that, I still think it's better for Python's paradigm.
I don't think this is a case of shoehorning anything. It is true that in Haskell you use currying more often, while in Python you usually supply a tuple with all the arguments at once.
However there are also cases in Python where you get currying, like decorators. And for complicated types like:
# greeting :: (str -> (str, str), int -> List[str]) -> Dict[str, int -> int]
being able to separate the signature from the declaration certainly makes reading it easier.
But in Python, to do currying, you either use decorators, functools.partial, or forced early binding (like nested function factories), etc. Just because currying is a thing you can do in the language (in a much different way than in Haskell where it is fundamental to every function) doesn't seem like a good reason to me to adopt Haskell-like type signatures.
Just for example, in Python it's extremely common to use default arguments, and in fact a lot of stuff in the typing module exists to make it easier to do something like make an Optional type that can either be `None` or a `str` or something. So whatever type signature you're going to mimic from Haskell, you won't be able to give optional types or default arguments without making it totally un-Haskell. And of course no one is going to go off and change Python to work based on Haskell-like type classes (nor should they).
They stuck with a syntax that was valid in older versions of Python, and used the function annotation work from Python 3.4. Thus, no "clean slate, choose the ideal syntax" discussion.