|
1 | 1 | import contextlib
|
| 2 | +import itertools |
2 | 3 | import os
|
3 | 4 | import re
|
| 5 | +import string |
4 | 6 | import tempfile
|
5 | 7 | import token
|
6 | 8 | import tokenize
|
@@ -3238,5 +3240,59 @@ def test_exact_flag(self):
|
3238 | 3240 | self.check_output(source, expect, flag)
|
3239 | 3241 |
|
3240 | 3242 |
|
| 3243 | +class StringPrefixTest(unittest.TestCase): |
| 3244 | + def test_prefixes(self): |
| 3245 | + # Get the list of defined string prefixes. I don't see an |
| 3246 | + # obvious documented way of doing this, but probably the best |
| 3247 | + # thing is to split apart tokenize.StringPrefix. |
| 3248 | + |
| 3249 | + # Make sure StringPrefix begins and ends in parens. |
| 3250 | + self.assertEqual(tokenize.StringPrefix[0], '(') |
| 3251 | + self.assertEqual(tokenize.StringPrefix[-1], ')') |
| 3252 | + |
| 3253 | + # Then split apart everything else by '|'. |
| 3254 | + defined_prefixes = set(tokenize.StringPrefix[1:-1].split('|')) |
| 3255 | + |
| 3256 | + # Now compute the actual string prefixes, by exec-ing all |
| 3257 | + # valid prefix combinations, followed by an empty string. |
| 3258 | + |
| 3259 | + # Try all prefix lengths until we find a length that has zero |
| 3260 | + # valid prefixes. This will miss the case where for example |
| 3261 | + # there are no valid 3 character prefixes, but there are valid |
| 3262 | + # 4 character prefixes. That seems extremely unlikely. |
| 3263 | + |
| 3264 | + # Note that the empty prefix is being included, because length |
| 3265 | + # starts at 0. That's expected, since StringPrefix includes |
| 3266 | + # the empty prefix. |
| 3267 | + |
| 3268 | + valid_prefixes = set() |
| 3269 | + for length in itertools.count(): |
| 3270 | + num_at_this_length = 0 |
| 3271 | + for prefix in ( |
| 3272 | + "".join(l) for l in list(itertools.combinations(string.ascii_lowercase, length)) |
| 3273 | + ): |
| 3274 | + for t in itertools.permutations(prefix): |
| 3275 | + for u in itertools.product(*[(c, c.upper()) for c in t]): |
| 3276 | + p = ''.join(u) |
| 3277 | + if p == "not": |
| 3278 | + # 'not' can never be a string prefix, |
| 3279 | + # because it's a valid expression: not "" |
| 3280 | + continue |
| 3281 | + try: |
| 3282 | + eval(f'{p}""') |
| 3283 | + |
| 3284 | + # No syntax error, so p is a valid string |
| 3285 | + # prefix. |
| 3286 | + |
| 3287 | + valid_prefixes.add(p) |
| 3288 | + num_at_this_length += 1 |
| 3289 | + except SyntaxError: |
| 3290 | + pass |
| 3291 | + if num_at_this_length == 0: |
| 3292 | + break |
| 3293 | + |
| 3294 | + self.assertEqual(defined_prefixes, valid_prefixes) |
| 3295 | + |
| 3296 | + |
3241 | 3297 | if __name__ == "__main__":
|
3242 | 3298 | unittest.main()
|
0 commit comments