|
10 | 10 | import subprocess |
11 | 11 | import sys |
12 | 12 | import platform |
| 13 | +import dis |
| 14 | +import linecache |
13 | 15 | from collections import OrderedDict, namedtuple |
14 | 16 | from collections.abc import Mapping |
15 | 17 | from itertools import filterfalse, chain |
@@ -1712,6 +1714,26 @@ def ruleorder(self, *rulenames): |
1712 | 1714 | def localrules(self, *rulenames): |
1713 | 1715 | self._localrules.update(rulenames) |
1714 | 1716 |
|
| 1717 | + def get_rule_source(self, func): |
| 1718 | + # This can't use `inspect` because the functions are compiled into intermediate python code |
| 1719 | + # in parser.py and that intermediate source is not available anymore (or desirable). |
| 1720 | + # Instead, we're using dis to retrieve the line numbers of the function in the intermediate |
| 1721 | + # code and then map it to the original file using `self.linemaps`. |
| 1722 | + sourcefile = func.__code__.co_filename |
| 1723 | + line_numbers = [] |
| 1724 | + linemap = self.linemaps[sourcefile] |
| 1725 | + for func_offset, line in dis.findlinestarts(func.__code__): |
| 1726 | + # The first instruction in the compiled function is RESUME, which |
| 1727 | + # with snakemake is mapped to the 'rule: ' line and is not considered |
| 1728 | + # part of the rule source. |
| 1729 | + if func_offset == 0: |
| 1730 | + continue |
| 1731 | + if line in linemap: |
| 1732 | + line_numbers.append(linemap[line]) |
| 1733 | + return "".join( |
| 1734 | + [linecache.getline(sourcefile, lineno) for lineno in sorted(line_numbers)] |
| 1735 | + ) |
| 1736 | + |
1715 | 1737 | def rule(self, name=None, lineno=None, snakefile=None, checkpoint=False): |
1716 | 1738 | # choose a name for an unnamed rule |
1717 | 1739 | if name is None: |
@@ -1930,6 +1952,8 @@ def check_may_use_software_deployment(method): |
1930 | 1952 | rule.name = ruleinfo.name |
1931 | 1953 | rule.docstring = ruleinfo.docstring |
1932 | 1954 | rule.run_func = ruleinfo.func |
| 1955 | + if rule.run_func is not None: |
| 1956 | + rule.run_func_src = self.get_rule_source(rule.run_func) |
1933 | 1957 | rule.shellcmd = ruleinfo.shellcmd |
1934 | 1958 | rule.script = ruleinfo.script |
1935 | 1959 | rule.notebook = ruleinfo.notebook |
|
0 commit comments