tokenize — 파이썬 소스용 토크나이저
tokenize — 파이썬 소스용 토크나이저
tokenize 모듈은 파이썬으로 구현된, 파이썬 소스 코드용 어휘 스캐너(lexical scanner)예요. 이 모듈의 스캐너는 주석도 토큰으로 반환해서, 화면 표시용 컬러라이저(colorizer)를 비롯한 "예쁘게 출력하는 도구"(pretty-printer)를 만드는 데 유용해요.
토큰 스트림 처리를 단순화하기 위해, 모든 연산자·구분자 토큰과 Ellipsis는 일반적인 OP 토큰 타입으로 반환됩니다. 정확한 타입은 tokenize.tokenize()가 반환하는 named tuple의 exact_type 속성을 확인하면 알 수 있어요.
경고: 이 모듈의 함수들은 문법적으로 유효한 파이썬 코드(
ast.parse()로 파싱할 때 예외가 발생하지 않는 코드)만 파싱하도록 설계됐어요. 잘못된 파이썬 코드를 주면 이 모듈 함수들의 동작은 정의되어 있지 않고 언제든 바뀔 수 있습니다.
본문
Tokenizing Input (입력 토큰화)
주 진입점은 제너레이터예요.
tokenize.tokenize(readline)
tokenize() 제너레이터는 인자 readline 하나를 요구하는데, 이는 파일 객체의 io.IOBase.readline() 메서드와 같은 인터페이스를 제공하는 callable 객체여야 해요. 함수에 대한 각 호출은 입력 한 줄을 bytes로 반환해야 합니다.
이 제너레이터는 5-튜플을 생성하는데, 각 멤버는: 토큰 타입; 토큰 문자열; 소스에서 토큰이 시작하는 행·열을 나타내는 정수 2-튜플 (srow, scol); 소스에서 토큰이 끝나는 행·열을 나타내는 정수 2-튜플 (erow, ecol); 그리고 토큰이 발견된 줄이에요. 전달되는 줄(마지막 튜플 항목)은 물리 줄(physical line)입니다. 이 5-튜플은 필드 이름이 type, string, start, end, line인 named tuple로 반환됩니다.
반환된 named tuple에는 OP 토큰의 정확한 연산자 타입을 담는 exact_type이라는 추가 속성이 있어요. 다른 모든 토큰 타입에서 exact_type은 named tuple의 type 필드와 같습니다.
버전 3.1에서 변경: named tuple 지원 추가. / 버전 3.3에서 변경:
exact_type지원 추가.
tokenize()는 PEP 263에 따라 UTF-8 BOM이나 인코딩 쿠키를 찾아 파일의 소스 인코딩을 결정해요.
tokenize.generate_tokens(readline)
bytes 대신 유니코드 문자열을 읽어 소스를 토큰화해요.
tokenize()와 마찬가지로 readline 인자는 입력 한 줄을 반환하는 callable이에요. 다만 generate_tokens()는 readline이 bytes가 아닌 str 객체를 반환하길 기대합니다.
결과는 tokenize()와 똑같이 named tuple을 생성하는 이터레이터예요. ENCODING 토큰은 생성하지 않아요.
token 모듈의 모든 상수도 tokenize에서 내보내집니다.
토큰화 과정을 역으로 수행하는 함수도 하나 더 제공돼요. 이는 스크립트를 토큰화하고, 토큰 스트림을 수정하고, 수정된 스크립트를 다시 쓰는 도구를 만드는 데 유용해요.
tokenize.untokenize(iterable)
토큰을 다시 파이썬 소스 코드로 변환해요. iterable은 최소 두 요소(토큰 타입과 토큰 문자열)를 가진 시퀀스를 반환해야 합니다. 추가 시퀀스 요소는 무시됩니다.
결과는 다시 토큰화했을 때 입력과 일치함이 보장돼서, 변환이 손실 없고 왕복(round-trip)이 보장됩니다. 이 보장은 토큰 타입과 토큰 문자열에만 적용되며, 토큰 사이의 간격(열 위치)은 바뀔 수 있어요.
tokenize()가 첫 토큰 시퀀스로 출력하는 ENCODING 토큰으로 인코딩된 bytes를 반환합니다. 입력에 인코딩 토큰이 없으면 대신 str을 반환해요.
tokenize()는 토큰화하는 소스 파일의 인코딩을 감지해야 해요. 이에 쓰는 함수도 제공됩니다.
tokenize.detect_encoding(readline)
detect_encoding() 함수는 파이썬 소스 파일을 디코딩하는 데 사용해야 할 인코딩을 감지하는 데 쓰여요. 인자 readline 하나를 요구하며, tokenize() 제너레이터와 같은 방식이에요.
readline을 최대 두 번 호출하고, 사용된 인코딩(문자열로)과 읽은 줄들(bytes로 디코딩되지 않은)의 리스트를 반환해요.
인코딩은 PEP 263에 명시된 대로 UTF-8 BOM이나 인코딩 쿠키의 존재로부터 감지됩니다. BOM과 쿠키가 모두 있는데 서로 일치하지 않으면 SyntaxError가 발생해요. BOM이 발견되면 'utf-8-sig'가 인코딩으로 반환된다는 점에 주의하세요.
인코딩이 지정되지 않으면 기본 'utf-8'이 반환됩니다.
파이썬 소스 파일을 열 때는 open()을 쓰세요 — open()이 detect_encoding()으로 파일 인코딩을 감지해요.
tokenize.open(filename)
detect_encoding()으로 감지된 인코딩으로 파일을 읽기 전용으로 엽니다.
버전 3.2에서 추가되었습니다.
exception tokenize.TokenError
여러 줄에 걸쳐 나뉠 수 있는 docstring이나 표현식이 파일 어디에서도 완료되지 않았을 때 발생해요. 예:
"""Beginning of
docstring
또는:
[1,
2,
3
Command-Line Usage (커맨드라인 사용)
버전 3.3에서 추가되었습니다.
tokenize 모듈은 커맨드라인에서 스크립트로 실행할 수 있어요. 아주 간단합니다.
python -m tokenize [-e] [filename.py]
다음 옵션이 허용됩니다.
-h, --help— 도움말 메시지를 보여주고 종료-e, --exact— 정확한 타입으로 토큰 이름 표시
filename.py가 지정되면 그 내용이 stdout으로 토큰화됩니다. 그렇지 않으면 stdin에서 토큰화가 수행돼요.
Examples (예시)
float 리터럴을 Decimal 객체로 변환하는 스크립트 재작성기의 예시:
from tokenize import tokenize, untokenize, NUMBER, STRING, NAME, OP
from io import BytesIO
def decistmt(s):
"""Substitute Decimals for floats in a string of statements.
>>> from decimal import Decimal
>>> s = 'print(+21.3e-5*-.1234/81.7)'
>>> decistmt(s)
"print (+Decimal ('21.3e-5')*-Decimal ('.1234')/Decimal ('81.7'))"
The format of the exponent is inherited from the platform C library.
Known cases are "e-007" (Windows) and "e-07" (not Windows). Since
we're only showing 12 digits, and the 13th isn't close to 5, the
rest of the output should be platform-independent.
>>> exec(s) #doctest: +ELLIPSIS
-3.21716034272e-0...7
Output from calculations with Decimal should be identical across all
platforms.
>>> exec(decistmt(s))
-3.217160342717258261933904529E-7
"""
result = []
g = tokenize(BytesIO(s.encode('utf-8')).readline) # tokenize the string
for toknum, tokval, _, _, _ in g:
if toknum == NUMBER and '.' in tokval: # replace NUMBER tokens
result.extend([
(NAME, 'Decimal'),
(OP, '('),
(STRING, repr(tokval)),
(OP, ')')
])
else:
result.append((toknum, tokval))
return untokenize(result).decode('utf-8')
커맨드라인에서 토큰화하는 예시. 이 스크립트:
def say_hello():
print("Hello, World!")
say_hello()
는 다음 출력으로 토큰화되는데, 첫 번째 열은 토큰이 발견된 줄/열 좌표의 범위, 두 번째 열은 토큰 이름, 마지막 열은 토큰 값(있는 경우)이에요.
$ python -m tokenize 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 'print'
2,9-2,10: OP '('
2,10-2,25: STRING '"Hello, World!"'
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 ''
정확한 토큰 타입 이름은 -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 'print'
2,9-2,10: LPAR '('
2,10-2,25: STRING '"Hello, World!"'
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 ''
파일을 프로그래매틱하게 토큰화하고, generate_tokens()로 bytes 대신 유니코드 문자열을 읽는 예시:
import tokenize
with tokenize.open('hello.py') as f:
tokens = tokenize.generate_tokens(f.readline)
for token in tokens:
print(token)
tokenize()로 bytes를 직접 읽는 방법도 있어요.
import tokenize
with open('hello.py', 'rb') as f:
tokens = tokenize.tokenize(f.readline)
for token in tokens:
print(token)