Skip to content

Commit 6eecceb

Browse files
authored
Merge pull request #350 from Carreau/share-magic
Share some options between the CLI and magic.
2 parents 1ca0962 + 266388e commit 6eecceb

File tree

2 files changed

+104
-15
lines changed

2 files changed

+104
-15
lines changed

pyinstrument/__main__.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -409,9 +409,31 @@ def store_and_consume_remaining(
409409
raise inner_exception
410410

411411

412+
class OptionsParseError(Exception):
413+
pass
414+
415+
412416
def compute_render_options(
413-
options: CommandLineOptions, renderer_class: type[renderers.Renderer], output_file: TextIO
417+
options: CommandLineOptions,
418+
renderer_class: type[renderers.Renderer],
419+
unicode_support: bool,
420+
color_support: bool,
414421
) -> dict[str, Any]:
422+
"""
423+
Given a list of `CommandLineOptions`, compute the
424+
rendering options for the given renderer.
425+
426+
Raises an `OptionsParseError` if there is an error parsing the options.
427+
428+
unicode_support:
429+
indicate whether the expected output supports unicode
430+
color_support:
431+
indicate whether the expected output supports color
432+
433+
Both of these will be used to determine the default of outputting unicode
434+
or color, but can be overridden with `options.color` and `option.unicode`.
435+
"""
436+
415437
# parse show/hide options
416438
if options.hide_fnmatch is not None and options.hide_regex is not None:
417439
raise OptionsParseError("You can‘t specify both --hide and --hide-regex")
@@ -449,8 +471,8 @@ def compute_render_options(
449471
if issubclass(renderer_class, renderers.ConsoleRenderer):
450472
unicode_override = options.unicode is not None
451473
color_override = options.color is not None
452-
unicode: Any = options.unicode if unicode_override else file_supports_unicode(output_file)
453-
color: Any = options.color if color_override else file_supports_color(output_file)
474+
unicode: Any = options.unicode if unicode_override else unicode_support
475+
color: Any = options.color if color_override else color_support
454476

455477
render_options.update({"unicode": unicode, "color": color})
456478

@@ -481,15 +503,14 @@ def compute_render_options(
481503
return render_options
482504

483505

484-
class OptionsParseError(Exception):
485-
pass
486-
487-
488506
def create_renderer(
489507
renderer_class: type[renderers.Renderer], options: CommandLineOptions, output_file: TextIO
490508
) -> renderers.Renderer:
491509
render_options = compute_render_options(
492-
options, renderer_class=renderer_class, output_file=output_file
510+
options,
511+
renderer_class=renderer_class,
512+
unicode_support=file_supports_unicode(output_file),
513+
color_support=file_supports_color(output_file),
493514
)
494515

495516
try:

pyinstrument/magic/magic.py

Lines changed: 75 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
from IPython.display import IFrame, display
1515

1616
from pyinstrument import Profiler, renderers
17+
from pyinstrument.__main__ import compute_render_options
1718
from pyinstrument.frame import Frame
1819
from pyinstrument.frame_ops import delete_frame_from_tree
1920
from pyinstrument.processors import ProcessorOptions
21+
from pyinstrument.renderers.console import ConsoleRenderer
22+
from pyinstrument.renderers.html import HTMLRenderer
2023

2124
_active_profiler = None
2225

@@ -76,6 +79,43 @@ def recreate_transformer(self, target_description: str):
7679
)
7780

7881
@magic_arguments()
82+
@argument(
83+
"-p",
84+
"--render-option",
85+
dest="render_options",
86+
action="append",
87+
metavar="RENDER_OPTION",
88+
type=str,
89+
help=(
90+
"options to pass to the renderer, in the format 'flag_name' or 'option_name=option_value'. "
91+
"For example, to set the option 'time', pass '-p time=percent_of_total'. To pass multiple "
92+
"options, use the -p option multiple times. You can set processor options using dot-syntax, "
93+
"like '-p processor_options.filter_threshold=0'. option_value is parsed as a JSON value or "
94+
"a string."
95+
),
96+
)
97+
@argument(
98+
"--show-regex",
99+
dest="show_regex",
100+
action="store",
101+
metavar="REGEX",
102+
help=(
103+
"regex matching the file paths whose frames to always show. "
104+
"Useful if --show doesn't give enough control."
105+
),
106+
)
107+
@argument(
108+
"--show",
109+
dest="show_fnmatch",
110+
action="store",
111+
metavar="EXPR",
112+
help=(
113+
"glob-style pattern matching the file paths whose frames to "
114+
"show, regardless of --hide or --hide-regex. For example, use "
115+
"--show '*/<library>/*' to show frames within a library that "
116+
"would otherwise be hidden."
117+
),
118+
)
79119
@argument(
80120
"--interval",
81121
type=float,
@@ -110,6 +150,26 @@ def recreate_transformer(self, target_description: str):
110150
nargs="*",
111151
help="When used as a line magic, the code to profile",
112152
)
153+
@argument(
154+
"--hide",
155+
dest="hide_fnmatch",
156+
action="store",
157+
metavar="EXPR",
158+
help=(
159+
"glob-style pattern matching the file paths whose frames to hide. Defaults to "
160+
"hiding non-application code"
161+
),
162+
)
163+
@argument(
164+
"--hide-regex",
165+
dest="hide_regex",
166+
action="store",
167+
metavar="REGEX",
168+
help=(
169+
"regex matching the file paths whose frames to hide. Useful if --hide doesn't give "
170+
"enough control."
171+
),
172+
)
113173
@no_var_expand
114174
@line_cell_magic
115175
def pyinstrument(self, line, cell=None):
@@ -126,6 +186,12 @@ def pyinstrument(self, line, cell=None):
126186
"""
127187
global _active_profiler
128188
args = parse_argstring(self.pyinstrument, line)
189+
190+
# 2024, always override this for now in IPython,
191+
# we can make an option later if necessary
192+
args.unicode = True
193+
args.color = True
194+
129195
ip = get_ipython()
130196

131197
if not ip:
@@ -175,10 +241,15 @@ def pyinstrument(self, line, cell=None):
175241
)
176242
return
177243

178-
html_renderer = renderers.HTMLRenderer(
179-
show_all=args.show_all,
180-
timeline=args.timeline,
244+
html_config = compute_render_options(
245+
args, renderer_class=HTMLRenderer, unicode_support=True, color_support=True
181246
)
247+
248+
text_config = compute_render_options(
249+
args, renderer_class=HTMLRenderer, unicode_support=True, color_support=True
250+
)
251+
252+
html_renderer = renderers.HTMLRenderer(show_all=args.show_all, timeline=args.timeline)
182253
html_renderer.preprocessors.append(strip_ipython_frames_processor)
183254
html_str = _active_profiler.output(html_renderer)
184255
as_iframe = IFrame(
@@ -188,10 +259,7 @@ def pyinstrument(self, line, cell=None):
188259
extras=['style="resize: vertical"', f'srcdoc="{html.escape(html_str)}"'],
189260
)
190261

191-
text_renderer = renderers.ConsoleRenderer(
192-
timeline=args.timeline,
193-
show_all=args.show_all,
194-
)
262+
text_renderer = renderers.ConsoleRenderer(**text_config)
195263
text_renderer.processors.append(strip_ipython_frames_processor)
196264

197265
as_text = _active_profiler.output(text_renderer)

0 commit comments

Comments
 (0)