Use the abstract syntax tree to check "undefined" variable names in Python

Source: Internet
Author: User

In fact, python is a real dynamic language. The variable name in the Code is not "declared" or "defined, the language itself does not provide any special syntax for declaring or defining variables (except global ). For programmers, This is a benefit and a danger, such as the following code:

Count = Total = 1
Delta = 0.7
While total <1000:
Total + = delta * (count * count + Delta * delta)
Dalta = delta * 1.1
Count * = dalta
Print total

The dalta behind the code is the result of Delta spelling errors. The program can run correctly or use the pychecker tool to check, but its output is obviously far from the expected correct result. Many people think that strict in Perl or option explicit in Visual Basic can help programmers reduce the chance of similar errors (although I don't think so), but in Python, because there is no syntax to explicitly define or declare the variable name, this kind of force check seems difficult to start-some solutions can be found online, but it is complicated, or use _ slots __, decorator is not easy to use to solve some problems.

The abstract syntax tree provided by parser or compiler package can be used to solve this problem easily. I roughly wrote a piece of code named strict. py. To change your program to the "Security Code" that forces the declaration of variables, we only need to follow strict. PY requirements, use _ Decl _ = "name1 name2... "In this way, you can declare the variable name before using the simple syntax. For example, you can add:

_ Decl _ = "delta total count"

Then use strict. py to check the Code Section (assuming the file name is test. py ):

Python strict. py test. py

We can see in the running results:

File 'test. py', line 6: Name 'dalta 'is not declared.

You can easily find out the variable name dalta, Which is misspelled, because the name dalta is not "declared" in advance ".

Strict. PY can also check the local name (_ Decl _ = "... "Such declaration statements can be used in any position in the Code), can recognize from... import, global, or function parameter table. Code like the following:

_ Decl _ = 'name1 name2 name3'

Name1 = 1
Name2 = 'jack'
Name3 = name1 + 3

Def Foo ():
Global name1
_ Decl _ = 'local _ name1 local_name2'
Name1 + = 4
Local_name1 = 1.2
Local_name2 = 'Mike'
Undeclared = 9

Strict. py can quickly find out where undeclared is the "undeclared" name.

Strict. PY only checks the names (L-value) that serve as the target of the value assignment. For reading a name, call a function name through obj. when the ATTR syntax accesses attributes or members of an object, strict. PY does not need to be considered-If undefined names appear in these cases, an error will be reported during compilation or running of the program, without causing potential risks.

I only tested strict. py in the python 2.4.3 environment and did not perform more complex tests. This code must have many improvements. First, list the strict. py code below:

Strict. py
------------------------------------------------------

Import sys
Import Compiler

Declaration_flag = "_ Decl __"

Def find_undeclared_names (AST, frames, is_decl ):

Next_frames = Frames

Def add_name (name ):
Frames [-1] [name] = true

Def find_name (name ):
Return frames [-1]. has_key (name)

Def get_alias (name_pair ):
If name_pair [1] is none:
Return name_pair [0]
Else:
Return name_pair [1]

If ast. _ class _. _ name _ = "assname ":
If not is_decl [0] and AST. Name = declaration_flag:
Is_decl [0] = true
Elif not find_name (AST. Name ):
Yield ast. Name, AST. lineno

Elif ast. _ class _. _ name _ = "Global ":
Map (add_name, AST. Names)

Elif ast. _ class _. _ name _ = "from ":
If (AST. Names [0] [0] = "*"):
MoD = _ import _ (AST. modname)
Map (add_name, filter (lambda X: Not X. startswith ('_'), Dir (MOD )))
Else:
Map (add_name, map (get_alias, AST. Names ))

Elif ast. _ class _. _ name _ = "const ":
If is_decl [0] and AST. value. _ class _. _ name _ = "str ":
Map (add_name, AST. value. Split ())
Is_decl [0] = false

Elif ast. _ class _. _ name _ = "function ":
Next_frames = frames + [dict (MAP (lambda X: (x, true), AST. argnames)]

Elif ast. _ class _. _ name _ = "class ":
Next_frames = frames + [{}]

For childnode in AST. getchildnodes ():
For X in find_undeclared_names (childnode, next_frames, is_decl ):
Yield x

If _ name _ = "_ main __":
If Len (SYS. argv )! = 2:
Print "Usage: Python strict. py <Python-source-File>"
Else:
For name, line_no in/
Find_undeclared_names (compiler. parsefile (SYS. argv [1]),
[{}],
[False]):
Print "File '% s', line % d: name' % s' is not declared." %/
(SYS. argv [1], line_no, name)

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.