55import html
66import importlib .resources
77import json
8+ import locale
89import math
910import os
1011import platform
1516from typing import Dict , List , Tuple
1617
1718from ._css_utils import get_combined_css
19+ from ._format_utils import fmt
1820from .collector import normalize_location , extract_lineno
1921from .stack_collector import StackTraceCollector
2022
@@ -343,7 +345,7 @@ def render_hierarchical_html(self, trees: Dict[str, TreeNode]) -> str:
343345 <div class="type-header" onclick="toggleTypeSection(this)">
344346 <span class="type-icon">{ icon } </span>
345347 <span class="type-title">{ type_names [module_type ]} </span>
346- <span class="type-stats">({ tree .count } { file_word } , { tree .samples :, } { sample_word } )</span>
348+ <span class="type-stats">({ tree .count } { file_word } , { tree .samples :n } { sample_word } )</span>
347349 </div>
348350 <div class="type-content"{ content_style } >
349351'''
@@ -390,7 +392,7 @@ def _render_folder(self, node: TreeNode, name: str, level: int = 1) -> str:
390392 parts .append (f'{ indent } <span class="folder-icon">▶</span>' )
391393 parts .append (f'{ indent } <span class="folder-name">📁 { html .escape (name )} </span>' )
392394 parts .append (f'{ indent } <span class="folder-stats">'
393- f'({ node .count } { file_word } , { node .samples :, } { sample_word } )</span>' )
395+ f'({ node .count } { file_word } , { node .samples :n } { sample_word } )</span>' )
394396 parts .append (f'{ indent } </div>' )
395397 parts .append (f'{ indent } <div class="folder-content" style="display: none;">' )
396398
@@ -435,7 +437,7 @@ def _render_file_item(self, stat: FileStats, indent: str = '') -> str:
435437
436438 return (f'{ indent } <div class="file-item">\n '
437439 f'{ indent } <a href="{ html_file } " class="file-link" title="{ full_path } ">📄 { module_name } </a>\n '
438- f'{ indent } <span class="file-samples">{ stat .total_samples :, } sample{ s } </span>\n '
440+ f'{ indent } <span class="file-samples">{ stat .total_samples :n } sample{ s } </span>\n '
439441 f'{ indent } <div class="heatmap-bar-container"><div class="heatmap-bar" style="width: { bar_width } px; height: { self .heatmap_bar_height } px;" data-intensity="{ intensity :.3f} "></div></div>\n '
440442 f'{ indent } </div>\n ' )
441443
@@ -826,7 +828,7 @@ def _generate_index_html(self, index_path: Path, file_stats: List[FileStats]):
826828 # Format error rate and missed samples with bar classes
827829 error_rate = self .stats .get ('error_rate' )
828830 if error_rate is not None :
829- error_rate_str = f"{ error_rate :.1f } %"
831+ error_rate_str = f"{ fmt ( error_rate ) } %"
830832 error_rate_width = min (error_rate , 100 )
831833 # Determine bar color class based on rate
832834 if error_rate < 5 :
@@ -842,7 +844,7 @@ def _generate_index_html(self, index_path: Path, file_stats: List[FileStats]):
842844
843845 missed_samples = self .stats .get ('missed_samples' )
844846 if missed_samples is not None :
845- missed_samples_str = f"{ missed_samples :.1f } %"
847+ missed_samples_str = f"{ fmt ( missed_samples ) } %"
846848 missed_samples_width = min (missed_samples , 100 )
847849 if missed_samples < 5 :
848850 missed_samples_class = "good"
@@ -861,10 +863,10 @@ def _generate_index_html(self, index_path: Path, file_stats: List[FileStats]):
861863 "<!-- INLINE_JS -->" : f"<script>\n { self ._template_loader .index_js } \n </script>" ,
862864 "<!-- PYTHON_LOGO -->" : self ._template_loader .logo_html ,
863865 "<!-- PYTHON_VERSION -->" : f"{ sys .version_info .major } .{ sys .version_info .minor } " ,
864- "<!-- NUM_FILES -->" : f"{ len (file_stats ):, } " ,
865- "<!-- TOTAL_SAMPLES -->" : f"{ self ._total_samples :, } " ,
866- "<!-- DURATION -->" : f" { self .stats .get ('duration_sec' , 0 ):,.1f } s" ,
867- "<!-- SAMPLE_RATE -->" : f" { self .stats .get ('sample_rate' , 0 ):,.1f } " ,
866+ "<!-- NUM_FILES -->" : f"{ len (file_stats ):n } " ,
867+ "<!-- TOTAL_SAMPLES -->" : f"{ self ._total_samples :n } " ,
868+ "<!-- DURATION -->" : fmt ( self .stats .get ('duration_sec' , 0 )) ,
869+ "<!-- SAMPLE_RATE -->" : fmt ( self .stats .get ('sample_rate' , 0 )) ,
868870 "<!-- ERROR_RATE -->" : error_rate_str ,
869871 "<!-- ERROR_RATE_WIDTH -->" : str (error_rate_width ),
870872 "<!-- ERROR_RATE_CLASS -->" : error_rate_class ,
@@ -908,12 +910,12 @@ def _generate_file_html(self, output_path: Path, filename: str,
908910 # Populate template
909911 replacements = {
910912 "<!-- FILENAME -->" : html .escape (filename ),
911- "<!-- TOTAL_SAMPLES -->" : f"{ file_stat .total_samples :, } " ,
912- "<!-- TOTAL_SELF_SAMPLES -->" : f"{ file_stat .total_self_samples :, } " ,
913- "<!-- NUM_LINES -->" : f"{ file_stat .num_lines :, } " ,
914- "<!-- PERCENTAGE -->" : f" { file_stat .percentage :.2f } " ,
915- "<!-- MAX_SAMPLES -->" : f"{ file_stat .max_samples :, } " ,
916- "<!-- MAX_SELF_SAMPLES -->" : f"{ file_stat .max_self_samples :, } " ,
913+ "<!-- TOTAL_SAMPLES -->" : f"{ file_stat .total_samples :n } " ,
914+ "<!-- TOTAL_SELF_SAMPLES -->" : f"{ file_stat .total_self_samples :n } " ,
915+ "<!-- NUM_LINES -->" : f"{ file_stat .num_lines :n } " ,
916+ "<!-- PERCENTAGE -->" : fmt ( file_stat .percentage , 2 ) ,
917+ "<!-- MAX_SAMPLES -->" : f"{ file_stat .max_samples :n } " ,
918+ "<!-- MAX_SELF_SAMPLES -->" : f"{ file_stat .max_self_samples :n } " ,
917919 "<!-- CODE_LINES -->" : '' .join (code_lines_html ),
918920 "<!-- INLINE_CSS -->" : f"<style>\n { self ._template_loader .file_css } \n </style>" ,
919921 "<!-- INLINE_JS -->" : f"<script>\n { self ._template_loader .file_js } \n </script>" ,
@@ -950,9 +952,9 @@ def _build_line_html(self, line_num: int, line_content: str,
950952 else :
951953 self_intensity = 0
952954
953- self_display = f"{ self_samples :, } " if self_samples > 0 else ""
954- cumulative_display = f"{ cumulative_samples :, } "
955- tooltip = f"Self: { self_samples :, } , Total: { cumulative_samples :, } "
955+ self_display = f"{ self_samples :n } " if self_samples > 0 else ""
956+ cumulative_display = f"{ cumulative_samples :n } "
957+ tooltip = f"Self: { self_samples :n } , Total: { cumulative_samples :n } "
956958 else :
957959 cumulative_intensity = 0
958960 self_intensity = 0
@@ -1197,7 +1199,7 @@ def _create_navigation_button(self, items_with_counts: List[Tuple[str, int, str,
11971199 file , line , func , count = valid_items [0 ]
11981200 target_html = self .file_index [file ]
11991201 nav_data = json .dumps ({'link' : f"{ target_html } #line-{ line } " , 'func' : func })
1200- title = f"Go to { btn_class } : { html .escape (func )} ({ count :, } samples)"
1202+ title = f"Go to { btn_class } : { html .escape (func )} ({ count :n } samples)"
12011203 return f'<button class="nav-btn { btn_class } " data-nav=\' { html .escape (nav_data )} \' title="{ title } ">{ arrow } </button>'
12021204
12031205 # Multiple items - create menu
@@ -1212,5 +1214,5 @@ def _create_navigation_button(self, items_with_counts: List[Tuple[str, int, str,
12121214 for file , line , func , count in valid_items
12131215 ]
12141216 items_json = html .escape (json .dumps (items_data ))
1215- title = f"{ len (items_data )} { btn_class } s ({ total_samples :, } samples)"
1217+ title = f"{ len (items_data )} { btn_class } s ({ total_samples :n } samples)"
12161218 return f'<button class="nav-btn { btn_class } " data-nav-multi=\' { items_json } \' title="{ title } ">{ arrow } </button>'
0 commit comments