|
20 | 20 |
|
21 | 21 | from __future__ import annotations
|
22 | 22 |
|
| 23 | +import re |
23 | 24 | from typing import TYPE_CHECKING
|
24 | 25 |
|
25 | 26 | import numpy # only to help intersphinx finding numpy doc
|
@@ -722,10 +723,40 @@ def label_cb(x, y):
|
722 | 723 | return ""
|
723 | 724 |
|
724 | 725 | else:
|
725 |
| - |
726 |
| - def label_cb(x, y): |
727 |
| - """Label callback""" |
728 |
| - return label % x |
| 726 | + # Test the label format once with a dummy value to determine the strategy |
| 727 | + dummy_x = 3.14159 |
| 728 | + try: |
| 729 | + _ = label % dummy_x # Test if old-style formatting works |
| 730 | + |
| 731 | + # If we get here, old-style formatting works fine |
| 732 | + def label_cb(x, y): |
| 733 | + """Label callback""" |
| 734 | + return label % x |
| 735 | + except (ValueError, TypeError): |
| 736 | + # If old-style formatting fails, prepare regex-based fallback |
| 737 | + |
| 738 | + # Pre-compile patterns for efficiency |
| 739 | + patterns = [ |
| 740 | + (re.compile(r"%g"), lambda x: str(x)), |
| 741 | + (re.compile(r"%f"), lambda x: f"{x:f}"), |
| 742 | + (re.compile(r"%d"), lambda x: f"{int(x):d}"), |
| 743 | + (re.compile(r"%.(\d+)f"), lambda x, m: f"{x:.{m.group(1)}f}"), |
| 744 | + (re.compile(r"%.(\d+)g"), lambda x, m: f"{x:.{m.group(1)}g}"), |
| 745 | + ] |
| 746 | + |
| 747 | + def label_cb(x, y): |
| 748 | + """Label callback with regex-based replacement""" |
| 749 | + result = label |
| 750 | + for pattern, replacement_func in patterns: |
| 751 | + if pattern.groups > 0: # Pattern with groups |
| 752 | + |
| 753 | + def repl(match): |
| 754 | + return replacement_func(x, match) |
| 755 | + |
| 756 | + result = pattern.sub(repl, result) |
| 757 | + else: # Simple pattern |
| 758 | + result = pattern.sub(lambda m: replacement_func(x), result) |
| 759 | + return result |
729 | 760 |
|
730 | 761 | return self.marker(
|
731 | 762 | position=(x, 0),
|
@@ -763,10 +794,41 @@ def label_cb(x, y):
|
763 | 794 | return ""
|
764 | 795 |
|
765 | 796 | else:
|
766 |
| - |
767 |
| - def label_cb(x, y): |
768 |
| - """Label callback""" |
769 |
| - return label % y |
| 797 | + # Test the label format once with a dummy value to determine the strategy |
| 798 | + dummy_y = 3.14159 |
| 799 | + try: |
| 800 | + _ = label % dummy_y # Test if old-style formatting works |
| 801 | + |
| 802 | + # If we get here, old-style formatting works fine |
| 803 | + def label_cb(x, y): |
| 804 | + """Label callback""" |
| 805 | + return label % y |
| 806 | + except (ValueError, TypeError): |
| 807 | + # If old-style formatting fails, prepare regex-based fallback |
| 808 | + import re |
| 809 | + |
| 810 | + # Pre-compile patterns for efficiency |
| 811 | + patterns = [ |
| 812 | + (re.compile(r"%g"), lambda y: str(y)), |
| 813 | + (re.compile(r"%f"), lambda y: f"{y:f}"), |
| 814 | + (re.compile(r"%d"), lambda y: f"{int(y):d}"), |
| 815 | + (re.compile(r"%.(\d+)f"), lambda y, m: f"{y:.{m.group(1)}f}"), |
| 816 | + (re.compile(r"%.(\d+)g"), lambda y, m: f"{y:.{m.group(1)}g}"), |
| 817 | + ] |
| 818 | + |
| 819 | + def label_cb(x, y): |
| 820 | + """Label callback with regex-based replacement""" |
| 821 | + result = label |
| 822 | + for pattern, replacement_func in patterns: |
| 823 | + if pattern.groups > 0: # Pattern with groups |
| 824 | + |
| 825 | + def repl(match): |
| 826 | + return replacement_func(y, match) |
| 827 | + |
| 828 | + result = pattern.sub(repl, result) |
| 829 | + else: # Simple pattern |
| 830 | + result = pattern.sub(lambda m: replacement_func(y), result) |
| 831 | + return result |
770 | 832 |
|
771 | 833 | return self.marker(
|
772 | 834 | position=(0, y),
|
@@ -806,10 +868,54 @@ def label_cb(x, y):
|
806 | 868 | return ""
|
807 | 869 |
|
808 | 870 | else:
|
809 |
| - |
810 |
| - def label_cb(x, y): |
811 |
| - """Label callback""" |
812 |
| - return label % (x, y) |
| 871 | + # Test the label format once with dummy values to determine the strategy |
| 872 | + dummy_x, dummy_y = 3.14159, 2.71828 |
| 873 | + try: |
| 874 | + _ = label % (dummy_x, dummy_y) # Test if old-style formatting works |
| 875 | + |
| 876 | + # If we get here, old-style formatting works fine |
| 877 | + def label_cb(x, y): |
| 878 | + """Label callback""" |
| 879 | + return label % (x, y) |
| 880 | + except (ValueError, TypeError): |
| 881 | + # If old-style formatting fails, prepare regex-based fallback |
| 882 | + import re |
| 883 | + |
| 884 | + # Pre-compile patterns for efficiency |
| 885 | + # For xcursor, handle both single and dual format specifiers |
| 886 | + single_patterns = [ |
| 887 | + (re.compile(r"%g"), lambda val: str(val)), |
| 888 | + (re.compile(r"%f"), lambda val: f"{val:f}"), |
| 889 | + (re.compile(r"%d"), lambda val: f"{int(val):d}"), |
| 890 | + (re.compile(r"%.(\d+)f"), lambda val, m: f"{val:.{m.group(1)}f}"), |
| 891 | + (re.compile(r"%.(\d+)g"), lambda val, m: f"{val:.{m.group(1)}g}"), |
| 892 | + ] |
| 893 | + |
| 894 | + def label_cb(x, y): |
| 895 | + """Label callback with regex-based replacement""" |
| 896 | + result = label |
| 897 | + # Apply single patterns, alternating between x and y values |
| 898 | + x_turn = True # Start with x |
| 899 | + for pattern, replacement_func in single_patterns: |
| 900 | + if pattern.groups > 0: # Pattern with groups |
| 901 | + |
| 902 | + def repl(match): |
| 903 | + nonlocal x_turn |
| 904 | + val = x if x_turn else y |
| 905 | + x_turn = not x_turn |
| 906 | + return replacement_func(val, match) |
| 907 | + |
| 908 | + result = pattern.sub(repl, result) |
| 909 | + else: # Simple pattern |
| 910 | + |
| 911 | + def repl(match): |
| 912 | + nonlocal x_turn |
| 913 | + val = x if x_turn else y |
| 914 | + x_turn = not x_turn |
| 915 | + return replacement_func(val) |
| 916 | + |
| 917 | + result = pattern.sub(repl, result) |
| 918 | + return result |
813 | 919 |
|
814 | 920 | return self.marker(
|
815 | 921 | position=(x, y),
|
|
0 commit comments