Is there any way to configure Python to detect all errors without having to execute the script?

1

I would like to know if it is possible to configure Python to warn of all the errors without having to execute the script. I already know that at the beginning of the execution it does a syntactic analysis. But I think that is not enough.

I mean also warn of lexical errors, dependencies and that kind of thing.

Well, when you have a small scritp maybe you do not care because the execution lasts little, but when you have 10 minutes of execution and suddenly you get an error like this, you feel like pulling your hair.

AttributeError: 'list' object has no attribute 'click'

Because now I correct it and I have to wait 10 more minutes for it to reach the same point and touch wood so that another error does not come out and have to start from the beginning. I see it that way, 6 mistakes = one lost hour of my life.

Well, that would be great if it could be configured to work in that way, just like C / C ++ compilers and other non-interpreted languages do.

If it can be done please tell me how. Thanks !!

    
asked by Iván Rodríguez 11.07.2018 в 03:23
source

2 answers

1

First, "all the errors without having to execute" is a little complicated, even C and C ++ which you mention have errors in runtime and that when compiled are not as explicit and informative as the ones that the Python interpreter shows you .

Now, in the error that I show, I would point out two aspects of Python that we must take into account, the first is that in Python they can add attributes or methods at runtime to a class or an object, so it is going to be difficult for a static analyzer to know whether or not the object has that method at any given time. For good and for bad, Python allows you to do things that are not allowed by C ++ or other compiled languages.

The second one is dynamic typing, strictly in Python the concept of "variable type" does not exist when using C, in Python the "variables" are names that are associated with an object in memory (a int is an object) and that can be assigned at any given time to another object without problems.

In Python 3 from PEP 483 - The Theory of Type Hints and with the implementation of the annotations has been introduced the possibility to specify the type of a variable / attribute as well as the return and arguments of a function / method.

Do not confuse yourself, Python is still strictly speaking a dynamically typed language and the interpreter does not perform any type checking on its own , but the above allows you to indicate the good reader of our code and tools designed to check types that our variable is expected to have or return our function, allowing a type check prior to generation of the bytecode and its execution.

There are IDEs like PyCharm that have their own type checker, showing you the warning in the editor itself, also based on all the above we have the MyPy project initially developed by DropBox and hosted in the same repository as Python currently, which allows the checking of static types in Python, a small example:

class Button:
    def click(self):
        print("Click")

class App:
    def __init__(self):
        self.btn= Button()


app = App()
app.btn.click() # Todo bien

app.btn = []    # Upsss!!!
app.btn.click()

The problem with the previous simple code, which is possibly similar to the problem that caused your error, is that the attribute btn must be an instance of Button , not any other thing like a list. There are many ways to validate and handle this at run time, but of course if we take half an hour to execute app.btn = [] , it takes half an hour for the error to appear ...

This is where type annotations and testers like MyPy come in, if we modify the self.btn statement by adding the corresponding type annotation:

self.btn: Button = Button()

when launching MyPy we will obtain our corresponding typing error without having to execute the code:

$ python -m mypy test.py

test.py:13: error: Incompatible types in assignment
(expression has type "List[<nothing>]", variable has type "Button")

Another example, a function that receives a list of integers or floats and returns the sum of its elements (Python > = 3.5):

from typing import List, Union

def suma(lista: List[Union[int, float]]) -> Union[int, float]:
    s =  sum(lista)
    return s

# Algunas llamadas a la función
suma([1, 2, 3])         # Correcta
suma((1, 2, 3))         # Incorrecta, se le pasa una tupla
suma([1.8, 4, 3])       # Correcta
suma([1.8, "hola", 3])  # Incorrecta, la lista contiene algo que no es int o float

Executing the previous code the interpreter only shows us the expected error when trying to concatenate floats with strings, error that is generated only when that line is executed, not before:

  

TypeError: unsupported operand type (s) for +: 'float' and 'str'

But MyPy goes further without executing the code showing us the two errors:

$ python -m mypy test.py
test.py:9: error: Argument 1 to "suma" has incompatible type
"Tuple[int, int, int]"; expected "List[Union[int, float]]"

test.py:11: error: List item 1 has incompatible type "str"; expected "float"

All this is relatively new in Python and is currently working on it, for example it is necessary that many third-party libraries implement the check for their own "types", for example imagine the previous case but with an array of NumPy that should contain int8 . Although it seems that the annotations are something superfluous they add a great potential, not only in the shown thing, but it allows for example the optimization of the code in compilers JIT which entailing an improvement in the yield, one of the advantages of the static typing.

    
answered by 11.07.2018 / 11:43
source
1

The quick answer is CAN NOT.

I know, you wonder why in the case of C/C++ the compiler detects many errors before actually executing the code and the example you have given is very good. The answer of this is the untyped nature of Python , that is, the possibility that the language gives you of not having to declare the type of each variable unlike others with C , C++ or Java among others. But .. What does the typing have to do with your question? Let's see a simple example:

Suppose this code C++ :

#include <iostream>
using namespace std;

class MiClase {
  public:
    void click (void);
};

void MiClase::click (void) {
  cout << "Click!!";
}

int main () {
  MiClase objeto1;
  int objeto2;

  objeto1.click();

  // Esto es erróneo el objeto2 es un int, obviamente no tiene un metodo click()
  objeto2.click();
  return 0;
}

This is compiled easily and quickly, for example: gcc test.cpp -lstdc++ , however, the compilation will throw an error:

test.cpp: In function ‘int main()’:
test.cpp:22:11: error: request for member ‘click’ in ‘objeto2’, which is of non-class type ‘int’
   objeto2.click();
           ^

What happens is quite obvious, objeto2 is a data type int that of course does not have a click() method (being strict even int is an object ). This validation is relatively easy to implement at compile time, at the moment we have written int objeto2; the compiler "saves" the reference to the name and the type, so whenever you see the variable objeto2 it is trivial to validate any use we make of it.

Now let's see a code quite similar to the previous one in a language not typed as Python .

class MiClase:
    def click (self):
        print("Click!!")

objeto1 = MiClase()
objeto1.click()

objeto1 = 1
# Esto fallará por que ahora objeto1 es un int
objeto1.click()

This will fail at runtime with the following exception:

Click!!
Traceback (most recent call last):
  File "test.py", line 12, in <module>
    objeto1.click()
AttributeError: 'int' object has no attribute 'click'

Note that objeto1.click() was executed, not the next call, since objeto1 has been redefined as int with objeto1 = 1 . And again a int does not have a method click() . For the compiler of an untyped language it is not easy to know that the second one is invalid, the only way it has is to execute the code and "see what happens". This example is simple and the error might seem obvious, however imagine more complex but very common cases: that the redefinition of objeto1 depends on the input of a user, the query to an api, the existence of a certain file or data , etc. You can imagine then that the only way that Python has to know what type an object is (and validate it) is to execute the code.

    
answered by 11.07.2018 в 05:31