tokenize --- Tokenizer cho nguồn Python

Source code: Lib/tokenize.py


Mô-đun tokenize cung cấp trình quét từ vựng cho mã nguồn Python, được triển khai bằng Python. Máy quét trong mô-đun này cũng trả về các nhận xét dưới dạng mã thông báo, giúp nó hữu ích cho việc triển khai "máy in đẹp", bao gồm cả bộ tạo màu cho hiển thị trên màn hình.

Để đơn giản hóa việc xử lý luồng mã thông báo, tất cả mã thông báo operator, delimiterEllipsis đều được trả về bằng loại mã thông báo OP chung. Loại chính xác có thể được xác định bằng cách kiểm tra thuộc tính exact_type trên named tuple được trả về từ tokenize.tokenize().

Cảnh báo

Lưu ý rằng các hàm trong mô-đun này chỉ được thiết kế để phân tích mã Python hợp lệ về mặt cú pháp (mã không tăng khi được phân tích cú pháp bằng ast.parse()). Hoạt động của các hàm trong mô-đun này là undefined khi cung cấp mã Python không hợp lệ và nó có thể thay đổi bất kỳ lúc nào.

Đầu vào mã thông báo

Điểm vào chính là generator:

tokenize.tokenize(readline)

Trình tạo tokenize() yêu cầu một đối số, readline, phải là một đối tượng có thể gọi được và cung cấp giao diện giống như phương thức io.IOBase.readline() của các đối tượng tệp. Mỗi lệnh gọi hàm sẽ trả về một dòng đầu vào dưới dạng byte.

Trình tạo tạo ra 5 bộ dữ liệu với các thành viên này: loại mã thông báo; chuỗi mã thông báo; một (srow, scol) 2 bộ dữ liệu int chỉ định hàng và cột nơi mã thông báo bắt đầu trong nguồn; một (erow, ecol) 2 bộ dữ liệu int chỉ định hàng và cột nơi mã thông báo kết thúc trong nguồn; và dòng mà mã thông báo được tìm thấy. Dòng được truyền (mục bộ cuối cùng) là dòng physical. Bộ thứ 5 được trả về dưới dạng named tuple với tên trường: type string start end line.

named tuple được trả về có một thuộc tính bổ sung có tên exact_type chứa loại toán tử chính xác cho mã thông báo OP. Đối với tất cả các loại mã thông báo khác, exact_type bằng với trường type tuple được đặt tên.

Thay đổi trong phiên bản 3.1: Đã thêm hỗ trợ cho các bộ dữ liệu được đặt tên.

Thay đổi trong phiên bản 3.3: Đã thêm hỗ trợ cho exact_type.

tokenize() xác định mã hóa nguồn của tệp bằng cách tìm kiếm UTF-8 BOM hoặc cookie mã hóa, theo PEP 263.

tokenize.generate_tokens(readline)

Mã hóa nguồn đọc chuỗi unicode thay vì byte.

Giống như tokenize(), đối số readline là một đối số có thể gọi được và trả về một dòng đầu vào. Tuy nhiên, generate_tokens() mong đợi readline trả về một đối tượng str thay vì byte.

Kết quả là một trình vòng lặp mang lại các bộ dữ liệu có tên, giống hệt như tokenize(). Nó không mang lại mã thông báo ENCODING.

Tất cả các hằng số từ mô-đun token cũng được xuất từ tokenize.

Một chức năng khác được cung cấp để đảo ngược quá trình mã hóa. Điều này hữu ích khi tạo các công cụ mã hóa tập lệnh, sửa đổi luồng mã thông báo và ghi lại tập lệnh đã sửa đổi.

tokenize.untokenize(iterable)

Chuyển đổi mã thông báo trở lại mã nguồn Python. Zz000zz phải trả về các chuỗi có ít nhất hai phần tử, loại mã thông báo và chuỗi mã thông báo. Mọi phần tử trình tự bổ sung đều bị bỏ qua.

Kết quả được đảm bảo mã hóa trở lại để khớp với đầu vào để việc chuyển đổi không bị mất và các chuyến đi khứ hồi được đảm bảo. Bảo đảm chỉ áp dụng cho loại mã thông báo và chuỗi mã thông báo vì khoảng cách giữa các mã thông báo (vị trí cột) có thể thay đổi.

Nó trả về byte, được mã hóa bằng mã thông báo ENCODING, đây là đầu ra chuỗi mã thông báo đầu tiên của tokenize(). Nếu không có mã thông báo mã hóa trong đầu vào thì thay vào đó, nó sẽ trả về một str.

tokenize() cần phát hiện mã hóa của tệp nguồn mà nó mã hóa. Chức năng nó sử dụng để thực hiện việc này có sẵn:

tokenize.detect_encoding(readline)

Hàm detect_encoding() được sử dụng để phát hiện mã hóa nên được sử dụng để giải mã tệp nguồn Python. Nó yêu cầu một đối số, readline, giống như cách tạo tokenize().

Nó sẽ gọi đường đọc tối đa hai lần và trả về mã hóa được sử dụng (dưới dạng chuỗi) và danh sách bất kỳ dòng nào (không được giải mã từ byte) mà nó đã đọc.

Nó phát hiện mã hóa từ sự hiện diện của UTF-8 BOM hoặc cookie mã hóa như được chỉ định trong PEP 263. Nếu có cả BOM và cookie nhưng không đồng ý thì SyntaxError sẽ được nâng lên. Lưu ý rằng nếu tìm thấy BOM, 'utf-8-sig' sẽ được trả về dưới dạng mã hóa.

Nếu không chỉ định mã hóa thì giá trị mặc định là 'utf-8' sẽ được trả về.

Sử dụng open() để mở tệp nguồn Python: nó sử dụng detect_encoding() để phát hiện mã hóa tệp.

tokenize.open(filename)

Mở tệp ở chế độ chỉ đọc bằng cách sử dụng mã hóa được phát hiện bởi detect_encoding().

Added in version 3.2.

exception tokenize.TokenError

Xảy ra khi một chuỗi tài liệu hoặc biểu thức có thể được chia thành nhiều dòng không được hoàn thành ở bất kỳ đâu trong tệp, ví dụ:

"""Bắt đầu
chuỗi tài liệu

hoặc:

[1,
 2,
 3

Sử dụng dòng lệnh

Added in version 3.3.

Mô-đun tokenize có thể được thực thi dưới dạng tập lệnh từ dòng lệnh. Nó đơn giản như:

python -m tokenize [-e] [filename.py]

Các tùy chọn sau được chấp nhận:

-h, --help

hiển thị thông báo trợ giúp này và thoát

-e, --exact

hiển thị tên mã thông báo bằng loại chính xác

Nếu filename.py được chỉ định, nội dung của nó sẽ được mã hóa thành thiết bị xuất chuẩn. Mặt khác, quá trình mã hóa được thực hiện trên stdin.

Ví dụ

Ví dụ về trình viết lại tập lệnh chuyển đổi các ký tự float thành các đối tượng thập phân:

từ tokenize nhập tokenize, unkenize, NUMBER, STRING, NAME, OP
từ io nhập ByteIO

def deistmt(s):
    """Thay thế số thập phân cho số float trong chuỗi câu lệnh.

    >>> từ nhập thập phân Thập phân
    >>> s = 'print(+21.3e-5*-.1234/81.7)'
    >>> quyết định
    "in (+Thập phân ('21.3e-5')*-Thập phân ('.1234')/Thập phân ('81.7'))"

    Định dạng của số mũ được kế thừa từ thư viện nền tảng C.
    Các trường hợp đã biết là "e-007" (Windows) và "e-07" (không phải Windows).  Kể từ khi
    chúng tôi chỉ hiển thị 12 chữ số và số 13 không gần bằng 5,
    phần còn lại của đầu ra phải độc lập với nền tảng.

    >>> (các) người thực thi #doctest: +ELLIPSIS
    -3.21716034272e-0...7

    Đầu ra từ các phép tính với Decimal phải giống hệt nhau trên tất cả
    nền tảng.

    >>> exec(decistmt(s))
    -3.217160342717258261933904529E-7
    """
    kết quả = []
    g = tokenize(BytesIO(s.encode('utf-8')).readline) # tokenize chuỗi
    cho toknum, tokval, _, _, _ trong g:
        nếu toknum == NUMBER  '.' trong tokval:  thông báo # replace NUMBER
            result.extend([
                (NAME, 'Thập phân'),
                (OP, '('),
                (STRING, đại diện (tokval)),
                (OP, ')')
            ])
        khác:
            result.append((toknum, tokval))
    trả về unkenize(result).decode('utf-8')

Ví dụ về mã thông báo từ dòng lệnh. Kịch bản:

chắc chắn say_hello():
    print("Xin chào thế giới!")

say_hello()

sẽ được mã hóa thành đầu ra sau trong đó cột đầu tiên là phạm vi tọa độ dòng/cột nơi tìm thấy mã thông báo, cột thứ hai là tên của mã thông báo và cột cuối cùng là giá trị của mã thông báo (nếu có)

$ python -m token hóa hello.py
0,0-0,0: ENCODING 'utf-8'
1,0-1,3: NAME 'def'
1,4-1,13: NAME 'say_hello'
1,13-1,14: OP '('
1,14-1,15: OP ')'
1,15-1,16: OP':'
1,16-1,17: NEWLINE '\n'
2,0-2,4: INDENT ''
2,4-2,9: NAME 'in'
2,9-2,10: OP '('
2,10-2,25: STRING '"Xin chào thế giới!"'
2,25-2,26: OP ')'
2,26-2,27: NEWLINE '\n'
3,0-3,1: NL '\n'
4,0-4,0: DEDENT''
4,0-4,9: NAME 'say_hello'
4,9-4,10: OP '('
4,10-4,11: OP ')'
4,11-4,12: NEWLINE '\n'
5,0-5,0: ENDMARKER''

Tên loại mã thông báo chính xác có thể được hiển thị bằng tùy chọn -e:

$ python -m tokenize -e hello.py
0,0-0,0: ENCODING 'utf-8'
1,0-1,3: NAME 'def'
1,4-1,13: NAME 'say_hello'
1,13-1,14: LPAR '('
1,14-1,15: RPAR ')'
1,15-1,16: COLON ':'
1,16-1,17: NEWLINE '\n'
2,0-2,4: INDENT ''
2,4-2,9: NAME 'in'
2,9-2,10: LPAR '('
2,10-2,25: STRING '"Xin chào thế giới!"'
2,25-2,26: RPAR ')'
2,26-2,27: NEWLINE '\n'
3,0-3,1: NL '\n'
4,0-4,0: DEDENT''
4,0-4,9: NAME 'say_hello'
4,9-4,10: LPAR '('
4,10-4,11: RPAR ')'
4,11-4,12: NEWLINE '\n'
5,0-5,0: ENDMARKER ''

Ví dụ về mã hóa tệp theo chương trình, đọc chuỗi unicode thay vì byte bằng generate_tokens():

nhập  thông báo

với tokenize.open('hello.py')  f:
     thông báo = tokenize.generate_tokens(f.readline)
    đối với  thông báo trong  thông báo:
        in ( thông báo)

Hoặc đọc byte trực tiếp với tokenize():

nhập  thông báo

với open('hello.py', 'rb')  f:
     thông báo = tokenize.tokenize(f.readline)
    đối với  thông báo trong  thông báo:
        in ( thông báo)