| 326 | import inspect |
| 327 | |
| 328 | def factory(source): |
| 329 | assert isinstance(source, types.FunctionType) |
| 330 | name = source.__name__ |
| 331 | source, _ = inspect.getsourcelines(source) |
| 332 | |
| 333 | # First, find the "def" line. |
| 334 | def_lineno = 0 |
| 335 | for line in source: |
| 336 | line = line.strip() |
| 337 | if line.startswith("def") and line.endswith(":"): |
| 338 | break |
| 339 | def_lineno += 1 |
| 340 | else: |
| 341 | raise ValueError("Failed to locate function header.") |
| 342 | |
| 343 | # Remove everything up to and including "def". |
| 344 | source = source[def_lineno + 1 :] |
| 345 | assert source |
| 346 | |
| 347 | # Now we need to adjust indentation. Compute how much the first line of |
| 348 | # the body is indented by, then dedent all lines by that amount. Blank |
| 349 | # lines don't matter indentation-wise, and might not be indented to begin |
| 350 | # with, so just replace them with a simple newline. |
| 351 | for line in source: |
| 352 | if line.strip(): |
| 353 | break # i.e.: use first non-empty line |
| 354 | indent = len(line) - len(line.lstrip()) |
| 355 | source = [l[indent:] if l.strip() else "\n" for l in source] |
| 356 | source = "".join(source) |
| 357 | |
| 358 | # Write it to file. |
| 359 | tmpfile = os.path.join(str(tmpdir), name + ".py") |
| 360 | assert not os.path.exists(tmpfile), "%s already exists." % (tmpfile,) |
| 361 | with open(tmpfile, "w") as stream: |
| 362 | stream.write(source) |
| 363 | |
| 364 | return tmpfile |
| 365 | |
| 366 | return factory |
| 367 | |