160 lines
5.4 KiB
Python
160 lines
5.4 KiB
Python
# This module contains utilities to compute loading statistics,
|
|
# like time spent visiting modules statically or dynamically.
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections import defaultdict
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING
|
|
|
|
from griffe._internal.enumerations import Kind
|
|
|
|
if TYPE_CHECKING:
|
|
from griffe._internal.loader import GriffeLoader
|
|
from griffe._internal.models import Alias, Object
|
|
|
|
|
|
class Stats:
|
|
"""Load statistics for a Griffe loader."""
|
|
|
|
def __init__(self, loader: GriffeLoader) -> None:
|
|
"""Initialiwe the stats object.
|
|
|
|
Parameters:
|
|
loader: The loader to compute stats for.
|
|
"""
|
|
self.loader = loader
|
|
"""The loader to compute stats for."""
|
|
|
|
modules_by_extension = defaultdict(
|
|
int,
|
|
{
|
|
"": 0,
|
|
".py": 0,
|
|
".pyi": 0,
|
|
".pyc": 0,
|
|
".pyo": 0,
|
|
".pyd": 0,
|
|
".so": 0,
|
|
},
|
|
)
|
|
|
|
top_modules = loader.modules_collection.members.values()
|
|
|
|
self.by_kind = {
|
|
Kind.MODULE: 0,
|
|
Kind.CLASS: 0,
|
|
Kind.FUNCTION: 0,
|
|
Kind.ATTRIBUTE: 0,
|
|
Kind.TYPE_ALIAS: 0,
|
|
}
|
|
"""Number of objects by kind."""
|
|
|
|
self.packages = len(top_modules)
|
|
"""Number of packages."""
|
|
|
|
self.modules_by_extension = modules_by_extension
|
|
"""Number of modules by extension."""
|
|
|
|
self.lines = sum(len(lines) for lines in loader.lines_collection.values())
|
|
"""Total number of lines."""
|
|
|
|
self.time_spent_visiting = 0
|
|
"""Time spent visiting modules."""
|
|
|
|
self.time_spent_inspecting = 0
|
|
"""Time spent inspecting modules."""
|
|
|
|
self.time_spent_serializing = 0
|
|
"""Time spent serializing objects."""
|
|
|
|
for module in top_modules:
|
|
self._itercount(module)
|
|
|
|
def _itercount(self, root: Object | Alias) -> None:
|
|
if root.is_alias:
|
|
return
|
|
self.by_kind[root.kind] += 1
|
|
if root.is_module:
|
|
if isinstance(root.filepath, Path):
|
|
self.modules_by_extension[root.filepath.suffix] += 1
|
|
elif root.filepath is None:
|
|
self.modules_by_extension[""] += 1
|
|
for member in root.members.values():
|
|
self._itercount(member)
|
|
|
|
def as_text(self) -> str:
|
|
"""Format the statistics as text.
|
|
|
|
Returns:
|
|
Text stats.
|
|
"""
|
|
lines = []
|
|
packages = self.packages
|
|
modules = self.by_kind[Kind.MODULE]
|
|
classes = self.by_kind[Kind.CLASS]
|
|
functions = self.by_kind[Kind.FUNCTION]
|
|
attributes = self.by_kind[Kind.ATTRIBUTE]
|
|
type_aliases = self.by_kind[Kind.TYPE_ALIAS]
|
|
objects = sum((modules, classes, functions, attributes, type_aliases))
|
|
lines.append("Statistics")
|
|
lines.append("---------------------")
|
|
lines.append("Number of loaded objects")
|
|
lines.append(f" Modules: {modules}")
|
|
lines.append(f" Classes: {classes}")
|
|
lines.append(f" Functions: {functions}")
|
|
lines.append(f" Attributes: {attributes}")
|
|
lines.append(f" Type aliases: {type_aliases}")
|
|
lines.append(f" Total: {objects} across {packages} packages")
|
|
per_ext = self.modules_by_extension
|
|
builtin = per_ext[""]
|
|
regular = per_ext[".py"]
|
|
stubs = per_ext[".pyi"]
|
|
compiled = modules - builtin - regular - stubs
|
|
lines.append("")
|
|
lines.append(f"Total number of lines: {self.lines}")
|
|
lines.append("")
|
|
lines.append("Modules")
|
|
lines.append(f" Builtin: {builtin}")
|
|
lines.append(f" Compiled: {compiled}")
|
|
lines.append(f" Regular: {regular}")
|
|
lines.append(f" Stubs: {stubs}")
|
|
lines.append(" Per extension:")
|
|
for ext, number in sorted(per_ext.items()):
|
|
if ext:
|
|
lines.append(f" {ext}: {number}")
|
|
|
|
visit_time = self.time_spent_visiting / 1000
|
|
inspect_time = self.time_spent_inspecting / 1000
|
|
total_time = visit_time + inspect_time
|
|
visit_percent = visit_time / total_time * 100
|
|
inspect_percent = inspect_time / total_time * 100
|
|
|
|
force_inspection = self.loader.force_inspection
|
|
visited_modules = 0 if force_inspection else regular
|
|
try:
|
|
visit_time_per_module = visit_time / visited_modules
|
|
except ZeroDivisionError:
|
|
visit_time_per_module = 0
|
|
|
|
inspected_modules = builtin + compiled + (regular if force_inspection else 0)
|
|
try:
|
|
inspect_time_per_module = inspect_time / inspected_modules
|
|
except ZeroDivisionError:
|
|
inspect_time_per_module = 0
|
|
|
|
lines.append("")
|
|
lines.append(
|
|
f"Time spent visiting modules ({visited_modules}): "
|
|
f"{visit_time}ms, {visit_time_per_module:.02f}ms/module ({visit_percent:.02f}%)",
|
|
)
|
|
lines.append(
|
|
f"Time spent inspecting modules ({inspected_modules}): "
|
|
f"{inspect_time}ms, {inspect_time_per_module:.02f}ms/module ({inspect_percent:.02f}%)",
|
|
)
|
|
|
|
serialize_time = self.time_spent_serializing / 1000
|
|
serialize_time_per_module = serialize_time / modules
|
|
lines.append(f"Time spent serializing: {serialize_time}ms, {serialize_time_per_module:.02f}ms/module")
|
|
|
|
return "\n".join(lines)
|