Add an optional static type checking function similar to PHP language for python

Add an optional static type checking function similar to PHP language for python

Now python already supports generic types and static checking functions, but most of these functions are used to support IDE and code editor plug-ins
I think: Can I add an option to python in the future, so that users can choose whether to enable the static checking function, use the type to find errors early, and there is a sample code at the end

  1. When the option is turned on:
    • a. If the user does not add type annotations to the function parameters, then at runtime, any type of parameter can be passed in. Only at runtime, inside a function, if the type does not match, an exception will be thrown, similar to statically compiled language
    • b. If the user adds a type annotation to the function parameter and the type does not match, an exception is thrown directly
  2. When the option is turned off:
    • Keep the current state: do not process type annotations, do not throw exceptions

Of course, the only performance of this function at present is: there is a red wavy line on the code editor plug-in, prompting me that the type does not match, but it is a real static checking function. If the code does not match the type at runtime, no exception will be thrown

Many times, I am very confused because of this, because it can only mention a hint for the verification plug-in, and cannot reduce the possibility of errors for my code

He is similar to the following possible options (usually declared at the top of the current code file)

  1. The "use strict" option in javascript
  2. The "#![allow(snake_case)]" option in rust
    Of course, estimate: Since python characters use "#" as a comment symbol, there is a high probability that the second method will be used
    This option can be added to the code to turn off the static checking function

:slight_smile: I love exploring uncharted territory

Source of idea: Some time ago, when I was learning the thinkphp framework of php, I encountered a route
GET request, he accepts a parameter userId to return the user’s information

Below is the sample code

public function index(int $id)
{
     $id = input('get.id');
     $ret = [
         'status' => 400,
         'message' => '',
     ];

     if (!isset($this->UserModel[$id])) {
         $ret['message'] = 'param error, $id is not exist';
         return json($ret);
     }

     return json([
         'status' => 200,
         'message' => 'success',
         'data' => $this->UserModel[$id],
     ]);
}

Regarding this function parameter, I only list two ways to define it

public function user($userId) // no type method
public function user(int $userId) // limit the type to `int`

For these two methods, the access address http://0.0.0.0:8000/user?id=, without rewriting the default exception

  1. If I do not define the type, the framework will not report an error, but interpret it as a null character and return
    { "status": 400, "message": "param error, $id is not exist" }
  2. If I define the type, the framework will report an error and have no return value, because the php framework will understand userId as a '' value, and the value of '' is a string type, which cannot be matched as int type

Actual scenario:

When this static type checking is in effect
I defined a function, if there is a type annotation, then when running the code with python, if the parameter type does not match, an exception will be thrown

Originally defined like this, it passes in a parameter of type int and returns a value of type int, which works as expected

def retNumber(playId: int, map={}) -> int:
     [
         map.update({i + 1: n}) for i, n in enumerate([
             1, 2, 5, 100, 1000, 9000, 10000, 1000000
         ])
     ]
     return map.get(playId, -1)

Modified (I currently installed python version is 3.10.12)

StrInt = TypeVar('StrInt', int, float, str)
# type T = int

def retNumber(playId: StrInt, map={}) -> int:
     [
         map.update(str({i + 1): n}) for i, n in enumerate([
             1, 2, 5, 100, 1000, 9000, 10000, 1000000
         ])
     ]
     return map.get(playId, -1)


print(retNumber(1)) # output: -1

At this time, I think that if I pass in an integer, then python should throw an exception directly. In fact, I think too much, it will not throw an exception, but return a -1 value


I have used pylint, but his configuration is too complicated and strict, and the flake8 plugin seems to be unable to check it out on the vscode editor. I use a generic type?

Sorry if the language organization is not in place!

Moving this to the Help category, there are already libraries that allow runtime type checking based on annotations, and runtime checking is not a goal of the typing included in Python itself.

“The type” of what? You understand that Python necessarily considers each piece of code individually, and does not have the greater context that the IDE does? It cannot possibly tell you that calling function X in an imported module will cause a type mismatch, because it cannot look at that module, because it has no way to know where that module will come from when the code actually runs, until it actually runs. The IDE is making an assumption that import foo refers to the foo.py file in your project; the compiler cannot be expected to break compilation of your file simply because it currently resides in a certain directory, when it could be the case that you will change that before running it anyway. Aside from that, there isn’t necessarily even a file; the REPL exists, after all.

The semantics of this are not even remotely similar to what Mypy and other such tools do. If they were, Typescript would not exist.

Well, my understanding is not deep enough

I came here to say the same thing!

I am aware that I can enforce types at run-time using external libraries such as @pydantic.validate_arguments, but given the popularity of pydantic, how often I use this at work, and the widespread availability of this in other languages, I think that this is a strong case for the standard library.

Something like @functools.enforce_types seems the most obvious to me.