Return a tuple (blocks, not_found) where ``blocks`` is a list of code fragments for each symbol parsed from code, and ``not_found`` are symbols not found in the code. For example:: In [1]: code = '''a = 10 ...: def b(): return 42 ...: class A: pa
(code, symbols)
| 84 | |
| 85 | |
| 86 | def extract_symbols(code, symbols): |
| 87 | """ |
| 88 | Return a tuple (blocks, not_found) |
| 89 | where ``blocks`` is a list of code fragments |
| 90 | for each symbol parsed from code, and ``not_found`` are |
| 91 | symbols not found in the code. |
| 92 | |
| 93 | For example:: |
| 94 | |
| 95 | In [1]: code = '''a = 10 |
| 96 | ...: def b(): return 42 |
| 97 | ...: class A: pass''' |
| 98 | |
| 99 | In [2]: extract_symbols(code, 'A,b,z') |
| 100 | Out[2]: (['class A: pass\\n', 'def b(): return 42\\n'], ['z']) |
| 101 | """ |
| 102 | symbols = symbols.split(',') |
| 103 | |
| 104 | # this will raise SyntaxError if code isn't valid Python |
| 105 | py_code = ast.parse(code) |
| 106 | |
| 107 | marks = [(getattr(s, 'name', None), s.lineno) for s in py_code.body] |
| 108 | code = code.split('\n') |
| 109 | |
| 110 | symbols_lines = {} |
| 111 | |
| 112 | # we already know the start_lineno of each symbol (marks). |
| 113 | # To find each end_lineno, we traverse in reverse order until each |
| 114 | # non-blank line |
| 115 | end = len(code) |
| 116 | for name, start in reversed(marks): |
| 117 | while not code[end - 1].strip(): |
| 118 | end -= 1 |
| 119 | if name: |
| 120 | symbols_lines[name] = (start - 1, end) |
| 121 | end = start - 1 |
| 122 | |
| 123 | # Now symbols_lines is a map |
| 124 | # {'symbol_name': (start_lineno, end_lineno), ...} |
| 125 | |
| 126 | # fill a list with chunks of codes for each requested symbol |
| 127 | blocks = [] |
| 128 | not_found = [] |
| 129 | for symbol in symbols: |
| 130 | if symbol in symbols_lines: |
| 131 | start, end = symbols_lines[symbol] |
| 132 | blocks.append('\n'.join(code[start:end]) + '\n') |
| 133 | else: |
| 134 | not_found.append(symbol) |
| 135 | |
| 136 | return blocks, not_found |
| 137 | |
| 138 | def strip_initial_indent(lines): |
| 139 | """For %load, strip indent from lines until finding an unindented line. |