|
1 |
| -"""Python interface for the 'lsprof' profiler. |
2 |
| - Compatible with the 'profile' module. |
3 |
| -""" |
4 |
| - |
5 |
| -__all__ = ["run", "runctx", "Profile"] |
6 |
| - |
7 |
| -import _lsprof |
8 |
| -import importlib.machinery |
9 |
| -import importlib.util |
10 |
| -import io |
11 |
| -import profile as _pyprofile |
12 |
| - |
13 |
| -# ____________________________________________________________ |
14 |
| -# Simple interface |
15 |
| - |
16 |
| -def run(statement, filename=None, sort=-1): |
17 |
| - return _pyprofile._Utils(Profile).run(statement, filename, sort) |
18 |
| - |
19 |
| -def runctx(statement, globals, locals, filename=None, sort=-1): |
20 |
| - return _pyprofile._Utils(Profile).runctx(statement, globals, locals, |
21 |
| - filename, sort) |
22 |
| - |
23 |
| -run.__doc__ = _pyprofile.run.__doc__ |
24 |
| -runctx.__doc__ = _pyprofile.runctx.__doc__ |
25 |
| - |
26 |
| -# ____________________________________________________________ |
27 |
| - |
28 |
| -class Profile(_lsprof.Profiler): |
29 |
| - """Profile(timer=None, timeunit=None, subcalls=True, builtins=True) |
30 |
| -
|
31 |
| - Builds a profiler object using the specified timer function. |
32 |
| - The default timer is a fast built-in one based on real time. |
33 |
| - For custom timer functions returning integers, timeunit can |
34 |
| - be a float specifying a scale (i.e. how long each integer unit |
35 |
| - is, in seconds). |
36 |
| - """ |
37 |
| - |
38 |
| - # Most of the functionality is in the base class. |
39 |
| - # This subclass only adds convenient and backward-compatible methods. |
40 |
| - |
41 |
| - def print_stats(self, sort=-1): |
42 |
| - import pstats |
43 |
| - if not isinstance(sort, tuple): |
44 |
| - sort = (sort,) |
45 |
| - pstats.Stats(self).strip_dirs().sort_stats(*sort).print_stats() |
46 |
| - |
47 |
| - def dump_stats(self, file): |
48 |
| - import marshal |
49 |
| - with open(file, 'wb') as f: |
50 |
| - self.create_stats() |
51 |
| - marshal.dump(self.stats, f) |
52 |
| - |
53 |
| - def create_stats(self): |
54 |
| - self.disable() |
55 |
| - self.snapshot_stats() |
| 1 | +"""Compatibility wrapper for cProfile module. |
56 | 2 |
|
57 |
| - def snapshot_stats(self): |
58 |
| - entries = self.getstats() |
59 |
| - self.stats = {} |
60 |
| - callersdicts = {} |
61 |
| - # call information |
62 |
| - for entry in entries: |
63 |
| - func = label(entry.code) |
64 |
| - nc = entry.callcount # ncalls column of pstats (before '/') |
65 |
| - cc = nc - entry.reccallcount # ncalls column of pstats (after '/') |
66 |
| - tt = entry.inlinetime # tottime column of pstats |
67 |
| - ct = entry.totaltime # cumtime column of pstats |
68 |
| - callers = {} |
69 |
| - callersdicts[id(entry.code)] = callers |
70 |
| - self.stats[func] = cc, nc, tt, ct, callers |
71 |
| - # subcall information |
72 |
| - for entry in entries: |
73 |
| - if entry.calls: |
74 |
| - func = label(entry.code) |
75 |
| - for subentry in entry.calls: |
76 |
| - try: |
77 |
| - callers = callersdicts[id(subentry.code)] |
78 |
| - except KeyError: |
79 |
| - continue |
80 |
| - nc = subentry.callcount |
81 |
| - cc = nc - subentry.reccallcount |
82 |
| - tt = subentry.inlinetime |
83 |
| - ct = subentry.totaltime |
84 |
| - if func in callers: |
85 |
| - prev = callers[func] |
86 |
| - nc += prev[0] |
87 |
| - cc += prev[1] |
88 |
| - tt += prev[2] |
89 |
| - ct += prev[3] |
90 |
| - callers[func] = nc, cc, tt, ct |
91 |
| - |
92 |
| - # The following two methods can be called by clients to use |
93 |
| - # a profiler to profile a statement, given as a string. |
94 |
| - |
95 |
| - def run(self, cmd): |
96 |
| - import __main__ |
97 |
| - dict = __main__.__dict__ |
98 |
| - return self.runctx(cmd, dict, dict) |
99 |
| - |
100 |
| - def runctx(self, cmd, globals, locals): |
101 |
| - self.enable() |
102 |
| - try: |
103 |
| - exec(cmd, globals, locals) |
104 |
| - finally: |
105 |
| - self.disable() |
106 |
| - return self |
107 |
| - |
108 |
| - # This method is more useful to profile a single function call. |
109 |
| - def runcall(self, func, /, *args, **kw): |
110 |
| - self.enable() |
111 |
| - try: |
112 |
| - return func(*args, **kw) |
113 |
| - finally: |
114 |
| - self.disable() |
115 |
| - |
116 |
| - def __enter__(self): |
117 |
| - self.enable() |
118 |
| - return self |
119 |
| - |
120 |
| - def __exit__(self, *exc_info): |
121 |
| - self.disable() |
122 |
| - |
123 |
| -# ____________________________________________________________ |
| 3 | +This module maintains backward compatibility by importing from the new |
| 4 | +profiling.tracing module. |
| 5 | +""" |
124 | 6 |
|
125 |
| -def label(code): |
126 |
| - if isinstance(code, str): |
127 |
| - return ('~', 0, code) # built-in functions ('~' sorts at the end) |
128 |
| - else: |
129 |
| - return (code.co_filename, code.co_firstlineno, code.co_name) |
| 7 | +from profiling.tracing import run, runctx, Profile |
130 | 8 |
|
131 |
| -# ____________________________________________________________ |
| 9 | +__all__ = ["run", "runctx", "Profile"] |
132 | 10 |
|
133 |
| -def main(): |
134 |
| - import os |
| 11 | +if __name__ == "__main__": |
135 | 12 | import sys
|
136 |
| - import runpy |
137 |
| - import pstats |
138 |
| - from optparse import OptionParser |
139 |
| - usage = "cProfile.py [-o output_file_path] [-s sort] [-m module | scriptfile] [arg] ..." |
140 |
| - parser = OptionParser(usage=usage) |
141 |
| - parser.allow_interspersed_args = False |
142 |
| - parser.add_option('-o', '--outfile', dest="outfile", |
143 |
| - help="Save stats to <outfile>", default=None) |
144 |
| - parser.add_option('-s', '--sort', dest="sort", |
145 |
| - help="Sort order when printing to stdout, based on pstats.Stats class", |
146 |
| - default=2, |
147 |
| - choices=sorted(pstats.Stats.sort_arg_dict_default)) |
148 |
| - parser.add_option('-m', dest="module", action="store_true", |
149 |
| - help="Profile a library module", default=False) |
150 |
| - |
151 |
| - if not sys.argv[1:]: |
152 |
| - parser.print_usage() |
153 |
| - sys.exit(2) |
154 |
| - |
155 |
| - (options, args) = parser.parse_args() |
156 |
| - sys.argv[:] = args |
157 |
| - |
158 |
| - # The script that we're profiling may chdir, so capture the absolute path |
159 |
| - # to the output file at startup. |
160 |
| - if options.outfile is not None: |
161 |
| - options.outfile = os.path.abspath(options.outfile) |
162 |
| - |
163 |
| - if len(args) > 0: |
164 |
| - if options.module: |
165 |
| - code = "run_module(modname, run_name='__main__')" |
166 |
| - globs = { |
167 |
| - 'run_module': runpy.run_module, |
168 |
| - 'modname': args[0] |
169 |
| - } |
170 |
| - else: |
171 |
| - progname = args[0] |
172 |
| - sys.path.insert(0, os.path.dirname(progname)) |
173 |
| - with io.open_code(progname) as fp: |
174 |
| - code = compile(fp.read(), progname, 'exec') |
175 |
| - spec = importlib.machinery.ModuleSpec(name='__main__', loader=None, |
176 |
| - origin=progname) |
177 |
| - module = importlib.util.module_from_spec(spec) |
178 |
| - # Set __main__ so that importing __main__ in the profiled code will |
179 |
| - # return the same namespace that the code is executing under. |
180 |
| - sys.modules['__main__'] = module |
181 |
| - # Ensure that we're using the same __dict__ instance as the module |
182 |
| - # for the global variables so that updates to globals are reflected |
183 |
| - # in the module's namespace. |
184 |
| - globs = module.__dict__ |
185 |
| - globs.update({ |
186 |
| - '__spec__': spec, |
187 |
| - '__file__': spec.origin, |
188 |
| - '__name__': spec.name, |
189 |
| - '__package__': None, |
190 |
| - '__cached__': None, |
191 |
| - }) |
192 |
| - |
193 |
| - try: |
194 |
| - runctx(code, globs, None, options.outfile, options.sort) |
195 |
| - except BrokenPipeError as exc: |
196 |
| - # Prevent "Exception ignored" during interpreter shutdown. |
197 |
| - sys.stdout = None |
198 |
| - sys.exit(exc.errno) |
199 |
| - else: |
200 |
| - parser.print_usage() |
201 |
| - return parser |
202 |
| - |
203 |
| -# When invoked as main program, invoke the profiler on a script |
204 |
| -if __name__ == '__main__': |
| 13 | + from profiling.tracing.__main__ import main |
205 | 14 | main()
|
0 commit comments