Represents a dynamically-loadable instrumentor. Handles version checking and instantiation of instrumentors.
| 450 | |
| 451 | @dataclass |
| 452 | class InstrumentorLoader: |
| 453 | """ |
| 454 | Represents a dynamically-loadable instrumentor. |
| 455 | Handles version checking and instantiation of instrumentors. |
| 456 | """ |
| 457 | |
| 458 | module_name: str |
| 459 | class_name: str |
| 460 | min_version: str |
| 461 | package_name: Optional[str] = None # Optional: actual pip package name |
| 462 | |
| 463 | @property |
| 464 | def module(self) -> ModuleType: |
| 465 | """Get the instrumentor module.""" |
| 466 | return importlib.import_module(self.module_name) |
| 467 | |
| 468 | @property |
| 469 | def should_activate(self) -> bool: |
| 470 | """Check if the package is available and meets version requirements.""" |
| 471 | try: |
| 472 | # Special case for stdlib modules (like concurrent.futures) |
| 473 | if self.package_name == "python": |
| 474 | import sys |
| 475 | |
| 476 | python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" |
| 477 | return Version(python_version) >= parse(self.min_version) |
| 478 | |
| 479 | # Use explicit package_name if provided, otherwise derive from module_name |
| 480 | if self.package_name: |
| 481 | provider_name = self.package_name |
| 482 | else: |
| 483 | provider_name = self.module_name.split(".")[-1] |
| 484 | |
| 485 | # Use common version utility |
| 486 | module_version = get_library_version(provider_name) |
| 487 | return module_version != "unknown" and Version(module_version) >= parse(self.min_version) |
| 488 | except Exception: |
| 489 | return False |
| 490 | |
| 491 | def get_instance(self) -> BaseInstrumentor: |
| 492 | """Create and return a new instance of the instrumentor.""" |
| 493 | return getattr(self.module, self.class_name)() |
| 494 | |
| 495 | |
| 496 | def instrument_one(loader: InstrumentorLoader) -> Optional[BaseInstrumentor]: |
no outgoing calls
no test coverage detected
searching dependent graphs…