FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[Original HTTPS Page]
cpython/Lib/tokenize.py at 3.14 · python/cpython · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
python
/
cpython
Public
Uh oh!
There was an error while loading.
Please reload this page
.
Notifications
You must be signed in to change notification settings
Fork
35.2k
Star
74.4k
Code
Issues
5k+
Pull requests
2.5k
Actions
Projects
Security and quality
0
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Security and quality
Insights
Expand file tree
Breadcrumbs
cpython
/
Lib
/
tokenize.py
Copy path
More file actions
More file actions
Latest commit
History
History
History
598 lines (520 loc) · 21.3 KB
Breadcrumbs
cpython
/
Lib
/
tokenize.py
Copy path
File metadata and controls
598 lines (520 loc) · 21.3 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
"""Tokenization help for Python programs.
tokenize(readline) is a generator that breaks a stream of bytes into
Python tokens. It decodes the bytes according to PEP-0263 for
determining source file encoding.
It accepts a readline-like method which is called repeatedly to get the
next line of input (or b"" for EOF). It generates 5-tuples with these
members:
the token type (see token.py)
the token (a string)
the starting (row, column) indices of the token (a 2-tuple of ints)
the ending (row, column) indices of the token (a 2-tuple of ints)
the original line (string)
It is designed to match the working of the Python tokenizer exactly, except
that it produces COMMENT tokens for comments and gives type OP for all
operators. Additionally, all token lists start with an ENCODING token
which tells you which encoding was used to decode the bytes stream.
"""
__author__
=
'Ka-Ping Yee <ping@lfw.org>'
__credits__
=
(
'GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, '
'Skip Montanaro, Raymond Hettinger, Trent Nelson, '
'Michael Foord'
)
from
builtins
import
open
as
_builtin_open
from
codecs
import
lookup
,
BOM_UTF8
import
collections
import
functools
from
io
import
TextIOWrapper
import
itertools
as
_itertools
import
re
import
sys
from
token
import
*
from
token
import
EXACT_TOKEN_TYPES
import
_tokenize
cookie_re
=
re
.
compile
(
br'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)'
,
re
.
ASCII
)
blank_re
=
re
.
compile
(
br'^[ \t\f]*(?:[#\r\n]|$)'
,
re
.
ASCII
)
import
token
__all__
=
token
.
__all__
+
[
"tokenize"
,
"generate_tokens"
,
"detect_encoding"
,
"untokenize"
,
"TokenInfo"
,
"open"
,
"TokenError"
]
del
token
class
TokenInfo
(
collections
.
namedtuple
(
'TokenInfo'
,
'type string start end line'
)):
def
__repr__
(
self
):
annotated_type
=
'%d (%s)'
%
(
self
.
type
,
tok_name
[
self
.
type
])
return
(
'TokenInfo(type=%s, string=%r, start=%r, end=%r, line=%r)'
%
self
.
_replace
(
type
=
annotated_type
))
@
property
def
exact_type
(
self
):
if
self
.
type
==
OP
and
self
.
string
in
EXACT_TOKEN_TYPES
:
return
EXACT_TOKEN_TYPES
[
self
.
string
]
else
:
return
self
.
type
def
group
(
*
choices
):
return
'('
+
'|'
.
join
(
choices
)
+
')'
def
any
(
*
choices
):
return
group
(
*
choices
)
+
'*'
def
maybe
(
*
choices
):
return
group
(
*
choices
)
+
'?'
# Note: we use unicode matching for names ("\w") but ascii matching for
# number literals.
Whitespace
=
r'[ \f\t]*'
Comment
=
r'#[^\r\n]*'
Ignore
=
Whitespace
+
any
(
r'\\\r?\n'
+
Whitespace
)
+
maybe
(
Comment
)
Name
=
r'\w+'
Hexnumber
=
r'0[xX](?:_?[0-9a-fA-F])+'
Binnumber
=
r'0[bB](?:_?[01])+'
Octnumber
=
r'0[oO](?:_?[0-7])+'
Decnumber
=
r'(?:0(?:_?0)*|[1-9](?:_?[0-9])*)'
Intnumber
=
group
(
Hexnumber
,
Binnumber
,
Octnumber
,
Decnumber
)
Exponent
=
r'[eE][-+]?[0-9](?:_?[0-9])*'
Pointfloat
=
group
(
r'[0-9](?:_?[0-9])*\.(?:[0-9](?:_?[0-9])*)?'
,
r'\.[0-9](?:_?[0-9])*'
)
+
maybe
(
Exponent
)
Expfloat
=
r'[0-9](?:_?[0-9])*'
+
Exponent
Floatnumber
=
group
(
Pointfloat
,
Expfloat
)
Imagnumber
=
group
(
r'[0-9](?:_?[0-9])*[jJ]'
,
Floatnumber
+
r'[jJ]'
)
Number
=
group
(
Imagnumber
,
Floatnumber
,
Intnumber
)
# Return the empty string, plus all of the valid string prefixes.
def
_all_string_prefixes
():
# The valid string prefixes. Only contain the lower case versions,
# and don't contain any permutations (include 'fr', but not
# 'rf'). The various permutations will be generated.
_valid_string_prefixes
=
[
'b'
,
'r'
,
'u'
,
'f'
,
't'
,
'br'
,
'fr'
,
'tr'
]
# if we add binary f-strings, add: ['fb', 'fbr']
result
=
{
''
}
for
prefix
in
_valid_string_prefixes
:
for
t
in
_itertools
.
permutations
(
prefix
):
# create a list with upper and lower versions of each
# character
for
u
in
_itertools
.
product
(
*
[(
c
,
c
.
upper
())
for
c
in
t
]):
result
.
add
(
''
.
join
(
u
))
return
result
@
functools
.
lru_cache
def
_compile
(
expr
):
return
re
.
compile
(
expr
,
re
.
UNICODE
)
# Note that since _all_string_prefixes includes the empty string,
# StringPrefix can be the empty string (making it optional).
StringPrefix
=
group
(
*
_all_string_prefixes
())
# Tail end of ' string.
Single
=
r"[^'\\]*(?:\\.[^'\\]*)*'"
# Tail end of " string.
Double
=
r'[^"\\]*(?:\\.[^"\\]*)*"'
# Tail end of ''' string.
Single3
=
r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''"
# Tail end of """ string.
Double3
=
r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""'
Triple
=
group
(
StringPrefix
+
"'''"
,
StringPrefix
+
'"""'
)
# Single-line ' or " string.
String
=
group
(
StringPrefix
+
r"'[^\n'\\]*(?:\\.[^\n'\\]*)*'"
,
StringPrefix
+
r'"[^\n"\\]*(?:\\.[^\n"\\]*)*"'
)
# Sorting in reverse order puts the long operators before their prefixes.
# Otherwise if = came before ==, == would get recognized as two instances
# of =.
Special
=
group
(
*
map
(
re
.
escape
,
sorted
(
EXACT_TOKEN_TYPES
,
reverse
=
True
)))
Funny
=
group
(
r'\r?\n'
,
Special
)
PlainToken
=
group
(
Number
,
Funny
,
String
,
Name
)
Token
=
Ignore
+
PlainToken
# First (or only) line of ' or " string.
ContStr
=
group
(
StringPrefix
+
r"'[^\n'\\]*(?:\\.[^\n'\\]*)*"
+
group
(
"'"
,
r'\\\r?\n'
),
StringPrefix
+
r'"[^\n"\\]*(?:\\.[^\n"\\]*)*'
+
group
(
'"'
,
r'\\\r?\n'
))
PseudoExtras
=
group
(
r'\\\r?\n|\z'
,
Comment
,
Triple
)
PseudoToken
=
Whitespace
+
group
(
PseudoExtras
,
Number
,
Funny
,
ContStr
,
Name
)
# For a given string prefix plus quotes, endpats maps it to a regex
# to match the remainder of that string. _prefix can be empty, for
# a normal single or triple quoted string (with no prefix).
endpats
=
{}
for
_prefix
in
_all_string_prefixes
():
endpats
[
_prefix
+
"'"
]
=
Single
endpats
[
_prefix
+
'"'
]
=
Double
endpats
[
_prefix
+
"'''"
]
=
Single3
endpats
[
_prefix
+
'"""'
]
=
Double3
del
_prefix
# A set of all of the single and triple quoted string prefixes,
# including the opening quotes.
single_quoted
=
set
()
triple_quoted
=
set
()
for
t
in
_all_string_prefixes
():
for
u
in
(
t
+
'"'
,
t
+
"'"
):
single_quoted
.
add
(
u
)
for
u
in
(
t
+
'"""'
,
t
+
"'''"
):
triple_quoted
.
add
(
u
)
del
t
,
u
tabsize
=
8
class
TokenError
(
Exception
):
pass
class
Untokenizer
:
def
__init__
(
self
):
self
.
tokens
=
[]
self
.
prev_row
=
1
self
.
prev_col
=
0
self
.
prev_type
=
None
self
.
prev_line
=
""
self
.
encoding
=
None
def
add_whitespace
(
self
,
start
):
row
,
col
=
start
if
row
<
self
.
prev_row
or
row
==
self
.
prev_row
and
col
<
self
.
prev_col
:
raise
ValueError
(
"start ({},{}) precedes previous end ({},{})"
.
format
(
row
,
col
,
self
.
prev_row
,
self
.
prev_col
))
self
.
add_backslash_continuation
(
start
)
col_offset
=
col
-
self
.
prev_col
if
col_offset
:
self
.
tokens
.
append
(
" "
*
col_offset
)
def
add_backslash_continuation
(
self
,
start
):
"""Add backslash continuation characters if the row has increased
without encountering a newline token.
This also inserts the correct amount of whitespace before the backslash.
"""
row
=
start
[
0
]
row_offset
=
row
-
self
.
prev_row
if
row_offset
==
0
:
return
newline
=
'
\r
\n
'
if
self
.
prev_line
.
endswith
(
'
\r
\n
'
)
else
'
\n
'
line
=
self
.
prev_line
.
rstrip
(
'
\\
\r
\n
'
)
ws
=
''
.
join
(
_itertools
.
takewhile
(
str
.
isspace
,
reversed
(
line
)))
self
.
tokens
.
append
(
ws
+
f"
\\
{
newline
}
"
*
row_offset
)
self
.
prev_col
=
0
def
escape_brackets
(
self
,
token
):
characters
=
[]
consume_until_next_bracket
=
False
for
character
in
token
:
if
character
==
"}"
:
if
consume_until_next_bracket
:
consume_until_next_bracket
=
False
else
:
characters
.
append
(
character
)
if
character
==
"{"
:
n_backslashes
=
sum
(
1
for
char
in
_itertools
.
takewhile
(
"
\\
"
.
__eq__
,
characters
[
-
2
::
-
1
]
)
)
if
n_backslashes
%
2
==
0
or
characters
[
-
1
]
!=
"N"
:
characters
.
append
(
character
)
else
:
consume_until_next_bracket
=
True
characters
.
append
(
character
)
return
""
.
join
(
characters
)
def
untokenize
(
self
,
iterable
):
it
=
iter
(
iterable
)
indents
=
[]
startline
=
False
for
t
in
it
:
if
len
(
t
)
==
2
:
self
.
compat
(
t
,
it
)
break
tok_type
,
token
,
start
,
end
,
line
=
t
if
tok_type
==
ENCODING
:
self
.
encoding
=
token
continue
if
tok_type
==
ENDMARKER
:
break
if
tok_type
==
INDENT
:
indents
.
append
(
token
)
continue
elif
tok_type
==
DEDENT
:
indents
.
pop
()
self
.
prev_row
,
self
.
prev_col
=
end
continue
elif
tok_type
in
(
NEWLINE
,
NL
):
startline
=
True
elif
startline
and
indents
:
indent
=
indents
[
-
1
]
if
start
[
1
]
>=
len
(
indent
):
self
.
tokens
.
append
(
indent
)
self
.
prev_col
=
len
(
indent
)
startline
=
False
elif
tok_type
in
{
FSTRING_MIDDLE
,
TSTRING_MIDDLE
}:
if
'{'
in
token
or
'}'
in
token
:
token
=
self
.
escape_brackets
(
token
)
last_line
=
token
.
splitlines
()[
-
1
]
end_line
,
end_col
=
end
extra_chars
=
last_line
.
count
(
"{{"
)
+
last_line
.
count
(
"}}"
)
end
=
(
end_line
,
end_col
+
extra_chars
)
self
.
add_whitespace
(
start
)
self
.
tokens
.
append
(
token
)
self
.
prev_row
,
self
.
prev_col
=
end
if
tok_type
in
(
NEWLINE
,
NL
):
self
.
prev_row
+=
1
self
.
prev_col
=
0
self
.
prev_type
=
tok_type
self
.
prev_line
=
line
return
""
.
join
(
self
.
tokens
)
def
compat
(
self
,
token
,
iterable
):
indents
=
[]
toks_append
=
self
.
tokens
.
append
startline
=
token
[
0
]
in
(
NEWLINE
,
NL
)
prevstring
=
False
in_fstring_or_tstring
=
0
for
tok
in
_itertools
.
chain
([
token
],
iterable
):
toknum
,
tokval
=
tok
[:
2
]
if
toknum
==
ENCODING
:
self
.
encoding
=
tokval
continue
if
toknum
in
(
NAME
,
NUMBER
):
tokval
+=
' '
# Insert a space between two consecutive strings
if
toknum
==
STRING
:
if
prevstring
:
tokval
=
' '
+
tokval
prevstring
=
True
else
:
prevstring
=
False
if
toknum
in
{
FSTRING_START
,
TSTRING_START
}:
in_fstring_or_tstring
+=
1
elif
toknum
in
{
FSTRING_END
,
TSTRING_END
}:
in_fstring_or_tstring
-=
1
if
toknum
==
INDENT
:
indents
.
append
(
tokval
)
continue
elif
toknum
==
DEDENT
:
indents
.
pop
()
continue
elif
toknum
in
(
NEWLINE
,
NL
):
startline
=
True
elif
startline
and
indents
:
toks_append
(
indents
[
-
1
])
startline
=
False
elif
toknum
in
{
FSTRING_MIDDLE
,
TSTRING_MIDDLE
}:
tokval
=
self
.
escape_brackets
(
tokval
)
# Insert a space between two consecutive brackets if we are in an f-string or t-string
if
tokval
in
{
"{"
,
"}"
}
and
self
.
tokens
and
self
.
tokens
[
-
1
]
==
tokval
and
in_fstring_or_tstring
:
tokval
=
' '
+
tokval
# Insert a space between two consecutive f-strings
if
toknum
in
(
STRING
,
FSTRING_START
)
and
self
.
prev_type
in
(
STRING
,
FSTRING_END
):
self
.
tokens
.
append
(
" "
)
toks_append
(
tokval
)
self
.
prev_type
=
toknum
def
untokenize
(
iterable
):
"""Transform tokens back into Python source code.
It returns a bytes object, encoded using the ENCODING
token, which is the first token sequence output by tokenize.
Each element returned by the iterable must be a token sequence
with at least two elements, a token number and token value. If
only two tokens are passed, the resulting output is poor.
The result is guaranteed to tokenize back to match the input so
that the conversion is lossless and round-trips are assured.
The guarantee applies only to the token type and token string as
the spacing between tokens (column positions) may change.
"""
ut
=
Untokenizer
()
out
=
ut
.
untokenize
(
iterable
)
if
ut
.
encoding
is
not
None
:
out
=
out
.
encode
(
ut
.
encoding
)
return
out
def
_get_normal_name
(
orig_enc
):
"""Imitates get_normal_name in Parser/tokenizer/helpers.c."""
# Only care about the first 12 characters.
enc
=
orig_enc
[:
12
].
lower
().
replace
(
"_"
,
"-"
)
if
enc
==
"utf-8"
or
enc
.
startswith
(
"utf-8-"
):
return
"utf-8"
if
enc
in
(
"latin-1"
,
"iso-8859-1"
,
"iso-latin-1"
)
or
\
enc
.
startswith
((
"latin-1-"
,
"iso-8859-1-"
,
"iso-latin-1-"
)):
return
"iso-8859-1"
return
orig_enc
def
detect_encoding
(
readline
):
"""
The detect_encoding() function is used to detect the encoding that should
be used to decode a Python source file. It requires one argument, readline,
in the same way as the tokenize() generator.
It will call readline a maximum of twice, and return the encoding used
(as a string) and a list of any lines (left as bytes) it has read in.
It detects the encoding from the presence of a utf-8 bom or an encoding
cookie as specified in pep-0263. If both a bom and a cookie are present,
but disagree, a SyntaxError will be raised. If the encoding cookie is an
invalid charset, raise a SyntaxError. Note that if a utf-8 bom is found,
'utf-8-sig' is returned.
If no encoding is specified, then the default of 'utf-8' will be returned.
"""
try
:
filename
=
readline
.
__self__
.
name
except
AttributeError
:
filename
=
None
bom_found
=
False
encoding
=
None
default
=
'utf-8'
def
read_or_stop
():
try
:
return
readline
()
except
StopIteration
:
return
b''
def
check
(
line
,
encoding
):
# Check if the line matches the encoding.
if
0
in
line
:
raise
SyntaxError
(
"source code cannot contain null bytes"
)
try
:
line
.
decode
(
encoding
)
except
UnicodeDecodeError
:
msg
=
"invalid or missing encoding declaration"
if
filename
is
not
None
:
msg
=
'{} for {!r}'
.
format
(
msg
,
filename
)
raise
SyntaxError
(
msg
)
def
find_cookie
(
line
):
match
=
cookie_re
.
match
(
line
)
if
not
match
:
return
None
encoding
=
_get_normal_name
(
match
.
group
(
1
).
decode
())
try
:
codec
=
lookup
(
encoding
)
except
LookupError
:
# This behaviour mimics the Python interpreter
if
filename
is
None
:
msg
=
"unknown encoding: "
+
encoding
else
:
msg
=
"unknown encoding for {!r}: {}"
.
format
(
filename
,
encoding
)
raise
SyntaxError
(
msg
)
if
bom_found
:
if
encoding
!=
'utf-8'
:
# This behaviour mimics the Python interpreter
if
filename
is
None
:
msg
=
'encoding problem: utf-8'
else
:
msg
=
'encoding problem for {!r}: utf-8'
.
format
(
filename
)
raise
SyntaxError
(
msg
)
encoding
+=
'-sig'
return
encoding
first
=
read_or_stop
()
if
first
.
startswith
(
BOM_UTF8
):
bom_found
=
True
first
=
first
[
3
:]
default
=
'utf-8-sig'
if
not
first
:
return
default
, []
encoding
=
find_cookie
(
first
)
if
encoding
:
check
(
first
,
encoding
)
return
encoding
, [
first
]
if
not
blank_re
.
match
(
first
):
check
(
first
,
default
)
return
default
, [
first
]
second
=
read_or_stop
()
if
not
second
:
check
(
first
,
default
)
return
default
, [
first
]
encoding
=
find_cookie
(
second
)
if
encoding
:
check
(
first
+
second
,
encoding
)
return
encoding
, [
first
,
second
]
check
(
first
+
second
,
default
)
return
default
, [
first
,
second
]
def
open
(
filename
):
"""Open a file in read only mode using the encoding detected by
detect_encoding().
"""
buffer
=
_builtin_open
(
filename
,
'rb'
)
try
:
encoding
,
lines
=
detect_encoding
(
buffer
.
readline
)
buffer
.
seek
(
0
)
text
=
TextIOWrapper
(
buffer
,
encoding
,
line_buffering
=
True
)
text
.
mode
=
'r'
return
text
except
:
buffer
.
close
()
raise
def
tokenize
(
readline
):
"""
The tokenize() generator requires one argument, readline, which
must be a callable object which provides the same interface as the
readline() method of built-in file objects. Each call to the function
should return one line of input as bytes. Alternatively, readline
can be a callable function terminating with StopIteration:
readline = open(myfile, 'rb').__next__ # Example of alternate readline
The generator produces 5-tuples with these members: the token type; the
token string; a 2-tuple (srow, scol) of ints specifying the row and
column where the token begins in the source; a 2-tuple (erow, ecol) of
ints specifying the row and column where the token ends in the source;
and the line on which the token was found. The line passed is the
physical line.
The first token sequence will always be an ENCODING token
which tells you which encoding was used to decode the bytes stream.
"""
encoding
,
consumed
=
detect_encoding
(
readline
)
rl_gen
=
_itertools
.
chain
(
consumed
,
iter
(
readline
,
b""
))
if
encoding
is
not
None
:
if
encoding
==
"utf-8-sig"
:
# BOM will already have been stripped.
encoding
=
"utf-8"
yield
TokenInfo
(
ENCODING
,
encoding
, (
0
,
0
), (
0
,
0
),
''
)
yield
from
_generate_tokens_from_c_tokenizer
(
rl_gen
.
__next__
,
encoding
,
extra_tokens
=
True
)
def
generate_tokens
(
readline
):
"""Tokenize a source reading Python code as unicode strings.
This has the same API as tokenize(), except that it expects the *readline*
callable to return str objects instead of bytes.
"""
return
_generate_tokens_from_c_tokenizer
(
readline
,
extra_tokens
=
True
)
def
_main
(
args
=
None
):
import
argparse
# Helper error handling routines
def
perror
(
message
):
sys
.
stderr
.
write
(
message
)
sys
.
stderr
.
write
(
'
\n
'
)
def
error
(
message
,
filename
=
None
,
location
=
None
):
if
location
:
args
=
(
filename
,)
+
location
+
(
message
,)
perror
(
"%s:%d:%d: error: %s"
%
args
)
elif
filename
:
perror
(
"%s: error: %s"
%
(
filename
,
message
))
else
:
perror
(
"error: %s"
%
message
)
sys
.
exit
(
1
)
# Parse the arguments and options
parser
=
argparse
.
ArgumentParser
(
color
=
True
)
parser
.
add_argument
(
dest
=
'filename'
,
nargs
=
'?'
,
metavar
=
'filename.py'
,
help
=
'the file to tokenize; defaults to stdin'
)
parser
.
add_argument
(
'-e'
,
'--exact'
,
dest
=
'exact'
,
action
=
'store_true'
,
help
=
'display token names using the exact type'
)
args
=
parser
.
parse_args
(
args
)
try
:
# Tokenize the input
if
args
.
filename
:
filename
=
args
.
filename
with
_builtin_open
(
filename
,
'rb'
)
as
f
:
tokens
=
list
(
tokenize
(
f
.
readline
))
else
:
filename
=
"<stdin>"
tokens
=
_generate_tokens_from_c_tokenizer
(
sys
.
stdin
.
readline
,
extra_tokens
=
True
)
# Output the tokenization
for
token
in
tokens
:
token_type
=
token
.
type
if
args
.
exact
:
token_type
=
token
.
exact_type
token_range
=
"%d,%d-%d,%d:"
%
(
token
.
start
+
token
.
end
)
print
(
"%-20s%-15s%-15r"
%
(
token_range
,
tok_name
[
token_type
],
token
.
string
))
except
IndentationError
as
err
:
line
,
column
=
err
.
args
[
1
][
1
:
3
]
error
(
err
.
args
[
0
],
filename
, (
line
,
column
))
except
TokenError
as
err
:
line
,
column
=
err
.
args
[
1
]
error
(
err
.
args
[
0
],
filename
, (
line
,
column
))
except
SyntaxError
as
err
:
error
(
err
,
filename
)
except
OSError
as
err
:
error
(
err
)
except
KeyboardInterrupt
:
print
(
"interrupted
\n
"
)
except
Exception
as
err
:
perror
(
"unexpected error: %s"
%
err
)
raise
def
_transform_msg
(
msg
):
"""Transform error messages from the C tokenizer into the Python tokenize
The C tokenizer is more picky than the Python one, so we need to massage
the error messages a bit for backwards compatibility.
"""
if
"unterminated triple-quoted string literal"
in
msg
:
return
"EOF in multi-line string"
return
msg
def
_generate_tokens_from_c_tokenizer
(
source
,
encoding
=
None
,
extra_tokens
=
False
):
"""Tokenize a source reading Python code as unicode strings using the internal C tokenizer"""
if
encoding
is
None
:
it
=
_tokenize
.
TokenizerIter
(
source
,
extra_tokens
=
extra_tokens
)
else
:
it
=
_tokenize
.
TokenizerIter
(
source
,
encoding
=
encoding
,
extra_tokens
=
extra_tokens
)
try
:
for
info
in
it
:
yield
TokenInfo
.
_make
(
info
)
except
SyntaxError
as
e
:
if
type
(
e
)
!=
SyntaxError
:
raise
e
from
None
msg
=
_transform_msg
(
e
.
msg
)
raise
TokenError
(
msg
, (
e
.
lineno
,
e
.
offset
))
from
None
if
__name__
==
"__main__"
:
_main
()
Back
|
FazBrowse Home
|
New Git URL