I am wrestling with the same problem and became tired of looking for a library to solve what I call the midi timing problem. I am now working on a midi file parser from scratch because only then I am certain that the tempo data will be taken into account properly when I parse the midi file.
The midi file exists in three formats, called type 0, type 1, and type 2.
A type 0 midi file consists of just one track, in which all the tempo change data is mixed with the notes, control changes, and all other kinds of events.
You are very likely using a type 1 midi file, which is also called a synchronous type midi file. This is because this type of midi file is intended for multi-track sequencers or score programs, like Musescore. This type is called 'synchronous' because any program reading this file splits it up into several tracks that are all supposed to play simultaneously.
In a type 1 midi file, the different tracks are written in different chunks. For each track, you have a different chunk, except the first chunk in the type 1 midi file. That is where you find the tempo change events.
This first track is reserved for all kinds of metadata, in particular, the tempo change data are present in only this first track.
The third type of midi file, type 2 is rarely used. It stores the midi data not as tracks that are supposed to play simultaneously (synchronous) but they are intended to be used one after the other (asynchronous). This type of midi file is mainly used to control all kinds of midi gear. To your midi gear up, for example before you record anything. Or to control lighting or other podium stuff.
Your problem is really a tough one. I have been searching, in vain, for a solution to parse a midi file with Python taking the tempo changes into account in a proper way.
The only practical way I have found is rather odd. I install Blender 2.80, an animation program. Then I install the latest version of Animation Nodes, a plugin for Blender 2.80. This version has a midi parser. But you must have the latest experimental build of animation nodes, otherwise, it will not work.
Then I can use the file reader node of animation nodes to get the notes, with the correct timings, as lists of notes with absolute times. I then use the Read Midi File node and load a midi file into it. I then attach a viewer node to the output. Then I go to the script editor and create a text block. Then I go to the tab Node, scroll down to the advanced section, click on 'text block', and then go to 'properties - text', and select that in the drop-down.
The note information with the right absolute timing, taking tempo change into account, appears as Python code.
You cannot install Animation Nodes in any later version than Blender 3.0. Moreover, if you install animation nodes, you must get a file from the install of animation nodes in blender 2.8 with the name 'midi.py' and overwrite it in the animation nodes install of Blender 3.0.
The only other way I have found to parse a midi file with the correct timings read from the tempo track is in a C# library called: DryWetMidi from Melanchall. And that is also a 'hassle' because to be able to use that with the correct timing you have to delve pretty deep in C#.
This is how far I got up till now.
To prevent all of this hassle, I started, with a deep sigh, to make my own midi file parser in Python. I have dived deep into this problem because it is crucial for me to make music animations based on midi files. Just look at my YouTube channel: 'Konrad Swart' to see why I want to solve this problem myself.
In any case, I hope this helps.
Your problem is not a simple problem!