Experimental completer for Python dictionary keys. Warning: This does an `eval` and `repr` on some Python expressions before the cursor, which is potentially dangerous. It doesn't match on function calls, so it only triggers attribute access.
| 295 | |
| 296 | |
| 297 | class DictionaryCompleter(Completer): |
| 298 | """ |
| 299 | Experimental completer for Python dictionary keys. |
| 300 | |
| 301 | Warning: This does an `eval` and `repr` on some Python expressions before |
| 302 | the cursor, which is potentially dangerous. It doesn't match on |
| 303 | function calls, so it only triggers attribute access. |
| 304 | """ |
| 305 | |
| 306 | def __init__( |
| 307 | self, |
| 308 | get_globals: Callable[[], dict[str, Any]], |
| 309 | get_locals: Callable[[], dict[str, Any]], |
| 310 | ) -> None: |
| 311 | super().__init__() |
| 312 | |
| 313 | self.get_globals = get_globals |
| 314 | self.get_locals = get_locals |
| 315 | |
| 316 | # Pattern for expressions that are "safe" to eval for auto-completion. |
| 317 | # These are expressions that contain only attribute and index lookups. |
| 318 | varname = r"[a-zA-Z_][a-zA-Z0-9_]*" |
| 319 | |
| 320 | expression = rf""" |
| 321 | # Any expression safe enough to eval while typing. |
| 322 | # No operators, except dot, and only other dict lookups. |
| 323 | # Technically, this can be unsafe of course, if bad code runs |
| 324 | # in `__getattr__` or ``__getitem__``. |
| 325 | ( |
| 326 | # Variable name |
| 327 | {varname} |
| 328 | |
| 329 | \s* |
| 330 | |
| 331 | (?: |
| 332 | # Attribute access. |
| 333 | \s* \. \s* {varname} \s* |
| 334 | |
| 335 | | |
| 336 | |
| 337 | # Item lookup. |
| 338 | # (We match the square brackets. The key can be anything. |
| 339 | # We don't care about matching quotes here in the regex. |
| 340 | # Nested square brackets are not supported.) |
| 341 | \s* \[ [^\[\]]+ \] \s* |
| 342 | )* |
| 343 | ) |
| 344 | """ |
| 345 | |
| 346 | # Pattern for recognizing for-loops, so that we can provide |
| 347 | # autocompletion on the iterator of the for-loop. (According to the |
| 348 | # first item of the collection we're iterating over.) |
| 349 | self.for_loop_pattern = re.compile( |
| 350 | rf""" |
| 351 | for \s+ ([a-zA-Z0-9_]+) \s+ in \s+ {expression} \s* : |
| 352 | """, |
| 353 | re.VERBOSE, |
| 354 | ) |
no outgoing calls
no test coverage detected