```python
"""Does @wraps actually copy __name__/__qualname__? Three handler shapes, three wrappers."""
from functools import wraps, partial

# ---------- three shapes a Sphinx handler can take ----------
def plain_handler(app):
    pass

class Collector:                      # callable object
    __module__ = "myext.collect"
    def __call__(self, app):
        pass

class Ext:
    def method(self, app):
        pass


# ---------- wrapper A: your current code (manual assignment) ----------
def wrap_manual(orig):
    handler_name = getattr(orig, "__qualname__",
                           getattr(orig, "__name__", repr(orig)))
    module = getattr(orig, "__module__", "unknown")

    def wrapped(app, *a, **kw):
        return orig(app, *a, **kw)

    wrapped.__name__ = getattr(orig, "__name__", "handler")
    wrapped.__qualname__ = handler_name
    wrapped.__module__ = module
    return wrapped


# ---------- wrapper B: @wraps alone ----------
def wrap_wraps(orig):
    @wraps(orig)
    def wrapped(app, *a, **kw):
        return orig(app, *a, **kw)
    return wrapped


# ---------- wrapper C: @wraps + fallback ----------
def wrap_wraps_fallback(orig):
    handler_name = getattr(orig, "__qualname__",
                           getattr(orig, "__name__", repr(orig)))

    @wraps(orig)
    def wrapped(app, *a, **kw):
        return orig(app, *a, **kw)

    if not hasattr(orig, "__qualname__"):
        wrapped.__name__ = handler_name
        wrapped.__qualname__ = handler_name
    return wrapped


def show(label, f):
    print(f"    {label:22} __name__={getattr(f, '__name__', '<missing>')!r}")
    print(f"    {'':22} __qualname__={getattr(f, '__qualname__', '<missing>')!r}")
    print(f"    {'':22} __module__={getattr(f, '__module__', '<missing>')!r}")


handlers = [
    ("plain function", plain_handler),
    ("callable object", Collector()),
    ("partial",         partial(Ext().method)),
    ("bound method",    Ext().method),
]

for label, h in handlers:
    print(f"=== {label} ===")
    print(f"  original has __qualname__? {hasattr(h, '__qualname__')}"
          f"   has __name__? {hasattr(h, '__name__')}")
    show("A manual", wrap_manual(h))
    show("B @wraps only", wrap_wraps(h))
    show("C @wraps + fallback", wrap_wraps_fallback(h))
    print()
```

---

- classifier handler:
    - put module name in place of extension name for "unknown"
    - themes -- get those from entry-points
    - check theme condition before "extension" condition : themes gets misclassified as an extension

git commit -m "fixed handler classification for themes and unknown modules, and switched to using entry-points for getting THEME_PACKAGES" \
           -m "Co-authored-by: Agriya Khetarpal <74401230+agriyakhetarpal@users.noreply.github.com>"

- use functools.wraps instead of manually reassigning modulename, qualname, and name etc.

- Handler wrapping happens on config-inited and builder-inited (such that handlers are not wrapped twice), which covers handlers registered during any extension's setup(). (maybe should run the wrapping function at each event emission? -- need to investigate further)
  - A better level could be to patch EventManager.connect (or app.connect) in the setup function to wrap the handlers at the registration time.


- [Friday] Fixed failing matplotlib builds and added benchmarks for it: module attr set to file name for when the module is None; and raising warning instead of exception if this benchmarking extension fails

---

- [Friday] account for gaps
    - add start and end timestamps for all events
    - then sort those by start time
    - then sum the gaps like a matrix where each element is the sum of gap durations between the i_th and j_th event 
- [Monaday] if an event emitted inside a handler
    - notion of depth and own_time vs total duration


- [Friday] extension wise summary table
    - sum all the durations of the handlers(or events?) by an extension(+theme) 

- docs : internal working
- tests?

---

Later PRs:
- parallel builds safe
- should i wrap app.build?
- CLI tool
- organising the code into different modules - classes.py, utils.py, extension.py

---

- Also wrapped emit and emit_firstresult to time the entire event and not just the handler time. (need to test it throughly on different projects-- for cases like-- if a handler function calls an emit in it?)
    - Maybe this needs some understanding of "depth". I read emit_firstresult in Sphinx and it itself calls self.emit, see: https://github.com/sphinx-doc/sphinx/blob/e44a40eb2f810558ccd9da1425421270ccb81351/sphinx/events.py#L482. So if a handler is emitting an event themselves, then the summary time won't be correct because you'd be counting twice by summing the durations. I think it is fine to defer this for now.

- [Partially Resolved-- there are gaps between events as well -- I'll investigate this further] Gaps between handlers aren't accounted for: This only measures time spent inside a handler call. Sphinx's core work that happens between events is not reflected in the final benchmarks, so per-event percentages here don't sum to the true build time. See the README diff of this PR for how this changes the reported %Build compared to the old event-wise numbers.
possible approach to solve this: combine the current approach with event-wise timing (wrapping app.events.emit/emit_firstresult directly maybe?) so event durations are measured correctly and inter-event gaps are reported explicitly, rather than inferred from handler.
    - fix number of calls--> bug: autodoc-process-signature is emitted twice (in the README). I'm not sure what exactly that is... but you can see that there is roughly half the unaccounted overhead, so it strongly suggests that.

---

- Put AboutTheCode.md on top of the extensions.py as a comment
- The benchmarks has:
    - a handler-wise break-down, 
    - then a summary table of how much each extension takes time 
    - and then a cumulative result of how much 


git clone git@github.com:pandas-dev/pandas.git
cd pandas
python3 -m venv ~/virtualenvs/pandas-dev
. ~/virtualenvs/pandas-dev/bin/activate
python -m pip install -r requirements-dev.txt
python -m pip install -ve . --no-build-isolation -Ceditable-verbose=true
python make.py html

----

tuesday:

git commit -m "replaced record_event with enter_ and exit_event: maintains a stack of currently in progress event emissions (events like a DFS-tree); now also keeping track of event's id, depth, parent_id and own_times; storing total_wall_time in the json; replaced print_summary with a separate script that prints the benchmarking summary, and printing gaps' summary table at the end, and printing number of emissions for each event; added try-except in setup()"

git commit -m "added/updated benchmarks for numpy, matplotlib and pandas"

git commit -m "updated usage docs and added docs describing the benchmarking output"

git commit -m "added docs on internal workings"

update PR description

---



---


    # Emissions still in progress when the records were written have a null
    # duration -- in a normal build that is just build-finished, from inside
    # which the file is written. They are skipped everywhere below.

The surrounding pair
    # of events is the only label available, and it is coarse: several events
    # fire mid-phase (include-read and object-description-transform during
    # parsing, doctree-read in the middle of the transform chain), so one
    # phase is split across several transitions here. See the core events
    # overview for where each event sits:
    # https://www.sphinx-doc.org/en/master/extdev/event_callbacks.html#core-events-overview

# Against the nested-inclusive total, since handler durations are
        # themselves inclusive: this is the gap between listeners.



**Handler times are inclusive.** If a handler triggers a nested emission, that nested time is
counted inside the handler's row *and* in the nested event's own block. Adding handler
totals across different events will over-count; use the per-event own times instead.

----

open issue in sphinx: (might be sphinx related issue)
- include-read event doesn't show up in the benchmarks even though files are added via the include directive 
- removed my extension from matplotlib and added this line to matplotlib's setup function-- it doesn't emit include-read event
- are there other events like this?

---

based on the given benchmarks output-- could you describe for a user facing docs-- like what are the benchmarks representins and how to read those-- keep it brief and strainght forward-- don't repeat yourself and keep it conversational and easy to understand for someone who don't understand the sphinx internal-- and refer the printing file to describe about how different things are calculated.


there are 2 tables:

- event wise break down (and then handler wise break-down for each event)
    - event details
    - handler details
    - unaccounted time etc.
- gaps breakdown
    - how is it calculated
    
above is based on a json generated:
    - wall_bild_time
    - events
    - handler calls

---

update this readme : write a good one liner that actually represent the current state of the benchmarks-- add external links if needed-- improve the usage section and add a section on how the extension calculates the benchmarks-- that goes over the internals of the actual implementations-- the wrapping of different things, the data classes, the envent logger and everything else-- but don't make it too long-- also mention all the edge cases and limitations of the extension-- like how build-finished event doesn't get recorded and more--adds overhead to the build process-- can't run in parallel, etc.; keep it easy for someone who is new to sphinx and user friendly and brief and don't repeat yourself-- conversational too


git commit -m"added basic tests"

git commit -m"added workflow test for style and pytests"

git commit -m"renamed extension and switched to hatchling"
