Add speedscope renderer by goxberry · Pull Request #160 · joerick/pyinstrument

joerick

joerick

joerick

This commit starts the implementation of a speedscope renderer by:

* copying the existing JSONRenderer to a new SpeedscopeRenderer class
  in `speedscope.py` of the `renderers` directory

* adding an import hook to `__init__.py` of the `renderers` directory

* adding a `speedscope` option to the `-r` flag at the command line

@goxberry

This commit deletes the jsonrenderer comment in the SpeedscopeRenderer
implementation; the source file is obviously not named jsonrenderer.
This commit converts SpeedscopeFrame from a non-class-style namedtuple
to a class-style namedtuple for readability.
This commit uncomments the SpeedscopeEventType enumeration class and
uses it in the SpeedscopeRenderer implementation.
This commit converts the SpeedscopeEvent namedtuple from
non-class-type to class-type to make it more self-documenting and to
conform with project guidelines regarding type hints.
This commit revises the docstring for SpeedscopeRenderer.render_frame
by deleting text that no longer applies to the implementation of this
method.
This commit uses the previously-unused profile_name object within
SpeedscopeRenderer.render.
This commit adds a JSON encoder class for the SpeedscopeEvent
namedtuple in order to stand up a SpeedscopeRenderer implementation
based on the Python json module.
This commit renames the _total_time field to _event_time in order to
clarify its purpose in the SpeedscopeRenderer class.
This commit replaces calls of the encode_speedscope_frame function
with json.dumps calls.
This commit deletes the encode_speedscope_event function because it is
no longer needed, and has been replaced with calls to json.dumps and
SpeedscopeEventEncoder.
This commit adds a JSON encoder for the SpeedscopeFrame class to try
to stand up a SpeedscopeRenderer implementation that uses the json
module.
This commit replaces calls of encode_frame with calls of json.dumps.
This commit deletes the encode_frame function because it is no longer
used.
This commit adds type hints to the Speedscope-related JSON encoders.
This commit deletes the import of `collections.namedtuple` because the
speedscope renderer now uses class-style namedtuple definitions.
This commit corrects the type hints by making each field a union type
with None, because `pyright` does not detect that `frame` cannot be
`None` where `SpeedscopeFrame.__init__` is invoked.
This commit removes type hints from `SpeedscopeEventType` in order to
silence some `pyright` errors regarding incorrect types when using
`str` as the type hint for each value.
This commit changes the SpeedscopeFrame and SpeedscopeEvent types from
class-style namedtuples to dataclasses because I couldn't figure out
how to serialize namedtuples to JSON objects via:

* subclassing json.JSONEncoder

* defining a default method

Attempting to return a dictionary using the data in each namedtuple
did not seem to yield a string containing a JSON object; instead, a
string containing a JSON array was returned.

Changing the namedtuple types to dataclasses and leveraging the
__dict__ dunder field, in concert with subclassing json.JSONEncoder
and defining a default method, yielded the desired result, although
memory usage will increase slightly.
This commit adds a SpeedscopeProfile class that stores the data
corresponding to speedscope "profile" objects, and adds a
SpeedscopeProfileEncoder class to serialize that class to JSON. The
encoder classes will be consolidated in a later commit.
This commit deletes the commented-out dead code used by the pure
string approach to serializing speedscope profile objects.
This commit deletes an orphaned LIFO iteration order comment about
dictionaries in Python 3.7+.
This commit adds a SpeedscopeFile data class and a SpeedscopeEncoder
class to serialize to JSON the SpeedscopeFrame, SpeedscopeEvent,
SpeedscopeProfile, SpeedscopeEventType, and SpeedscopeFile data
classes.
This commit revises the SpeedscopeRenderer class by removing a lot of
dead code and updating docstrings and comments in the class and its
auxiliary classes.
This commit deletes the processor.aggregate_repeated_calls method from
the list of default processors returned by SpeedscopeRenderer because
speedscope is a timeline-based format, and aggregating repeated calls
fouls up a timeline view.
This commit updates a code comment within SpeedscopeRenderer.render
discussing why the frame list is constructed as it is.
This commit fixes some pyright errors in
pyinstrument/renderers/speedscope.py.
This commit replaces the for loop that builds up the speedscope frame
list with a list comprehension.
This commit adds SpeedscopeRenderer and the `-r speedscope` flag to
the pyinstrument documentation.
This commit removes the dataclass arguments from the SpeedscopeEvent
class because this class does not need to be hashable.
This commit modifies the display title of a speedscope profile
exported from pyinstrument to include the timestamp of when the
profile was generated (which also happens to be argument to
`--load-prev` needed to render output in other formats).
This commit changes the SpeedscopeRenderer code to comply with project
style guidelines regarding code formatting with black and isort.
This commit adds SpeedscopeRenderer to the overflow test in
pyinstrument's test suite.
This commit fixes an apparent inconsistency in the
profiles[0].endValue field of the Speedscope output from
SpeedscopeRenderer. In unit testing, the value of session.duration
(within a call to SpeedscopeRenderer.render) may not be equal to the
event time of the last event in the profiles[0].events field of the
Speedscope output. In fact, in the test_speedscope_output test within
test_profiler.py, the value of session duration was approximately
0.0047s (in local testing on my laptop), whereas the time value of the
last event generated by the profile in that test should be 0.75s +/-
0.3s.

To correct this inconsistency, the end value of profile is set equal
to the event time of the last event.
This commit adds a test to test_profiler that tests the JSON output
emitted by SpeedscopeRenderer against known properties it should have.
This commit:

* Removes, in `test/test_profiler.py`, the test of the value of the
  `"profiles[0].endValue"` field in the Speedscope JSON output. The
  difference between the value of `profiles[0].endValue` (equal to
  `session.duration` from the `session` argument passed to
  `SpeedscopeRenderer.render`) and the time of the last event can be
  attributed to the `fake_time` context manager used as a mock timer in
  the profiler/renderer tests.

* Changes the value assigned to the `profiles[0].endValue` field via
  the last positional argument to `SpeedscopeProfile.__init__` from
  `self._event_time` (which, at that point in
  `SpeedscopeRenderer.render` equals the last event time) back to
  `session.duration`. This change is made because the discrepancy
  between the values of `session.duration` and `self._event_time` can
  be attributed to the `fake_time` context manager used as a mock
  timer in the profiler/renderer tests.
This commit aims to make the `test_speedscope_output` test in
`test/test_profiler.py` less wordy and more readable by:

* deleting message arguments to assertions

* replacing local variables with literals or expressions, because many
  of these variables were motivated by keeping line length low in
  message arguments passed to assertions

* removing tolerances in the pytest.approx calls because CI jitter
  should not affect timings, and all floating point numbers involved
  are exactly representable per the IEEE-754 standard