svg-ultralight 0.41.0__py3-none-any.whl → 0.42.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of svg-ultralight might be problematic. Click here for more details.

@@ -27,10 +27,88 @@ if TYPE_CHECKING:
27
27
  )
28
28
 
29
29
 
30
- def format_number(num: float | str) -> str:
30
+ _MAYBE_NEG = r"(?:(?P<negative>-?))"
31
+ _MAYBE_INT = r"(?:(?P<integer>\d+?))"
32
+ _MAYBE_FRACTION = r"(?:\.(?P<fraction>\d+))?"
33
+ _MAYBE_EXP = r"(?:[eE](?P<exponent>[+-]?\d+))?"
34
+
35
+ # Split a float (fp or exponential) into its components. All components are optional.
36
+ FLOAT_PATTERN = re.compile(rf"{_MAYBE_NEG}{_MAYBE_INT}{_MAYBE_FRACTION}{_MAYBE_EXP}")
37
+
38
+
39
+ def _split_float_str(num: str | float) -> tuple[str, str, str, int]:
40
+ """Split a float string into its sign, integer part, fractional part, and exponent.
41
+
42
+ :param num_str: A string representing the number (e.g., '1.23e+03').
43
+ :return: A tuple containing the integer part, fractional part, and exponent.
44
+ """
45
+ if float(num) == 0:
46
+ return "", "", "", 0
47
+ num_str = str(num)
48
+ groups = FLOAT_PATTERN.fullmatch(num_str)
49
+ if not groups:
50
+ msg = "Invalid number string: {num_str}."
51
+ raise ValueError(msg.format(num_str=num_str))
52
+
53
+ sign = groups["negative"] or ""
54
+ integer = (groups["integer"] or "").lstrip("0")
55
+ fraction = (groups["fraction"] or "").rstrip("0")
56
+ exponent = int(groups["exponent"] or 0)
57
+ return sign, integer, fraction, exponent
58
+
59
+
60
+ def _format_as_fixed_point(num: str | float) -> str:
61
+ """Format a number in fixed-point notation.
62
+
63
+ :param exp_str: A string representing the number in exponential notation
64
+ (e.g., '1.23e+03') or just a number.
65
+ :return: A string representing the number in fixed-point notation.
66
+ """
67
+ sign, integer, fraction, exponent = _split_float_str(num)
68
+ if exponent > 0:
69
+ fraction = fraction.ljust(exponent, "0")
70
+ integer += fraction[:exponent]
71
+ fraction = fraction[exponent:]
72
+ elif exponent < 0:
73
+ integer = integer.rjust(-exponent, "0")
74
+ fraction = integer[exponent:] + fraction
75
+ integer = integer[:exponent]
76
+
77
+ fraction = "." + fraction if fraction else ""
78
+ return f"{sign}{integer}{fraction}" or "0"
79
+
80
+
81
+ def _format_as_exponential(num: str | float) -> str:
82
+ """Convert a number in fixed-point notation (as a string) to exponential notation.
83
+
84
+ :param num_str: A string representing the number in fixed-point notation
85
+ (e.g., '123000') or just a number.
86
+ :return: A string representing the number in exponential notation.
87
+ """
88
+ sign, integer, fraction, exponent = _split_float_str(num)
89
+ if len(integer) > 1:
90
+ exponent += len(integer) - 1
91
+ fraction = (integer[1:] + fraction).rstrip("0")
92
+ integer = integer[0]
93
+ elif not integer and fraction:
94
+ leading_zeroes = len(fraction) - len(fraction.lstrip("0"))
95
+ exponent -= leading_zeroes + 1
96
+ integer = fraction[leading_zeroes]
97
+ fraction = fraction[leading_zeroes + 1 :]
98
+
99
+ fraction = "." + fraction if fraction else ""
100
+ exp_str = f"e{exponent}" if exponent else ""
101
+ return f"{sign}{integer}{fraction}{exp_str}" or "0"
102
+
103
+
104
+ def format_number(num: float | str, precision: float | None = 6) -> str:
31
105
  """Format strings at limited precision.
32
106
 
33
107
  :param num: anything that can print as a float.
108
+ :param precision: number of digits after the decimal point, default 6. You can
109
+ also pass None for no precision limit. This may produce some long strings,
110
+ but will retain as much information as possible when converting between
111
+ floats and strings.
34
112
  :return: str
35
113
 
36
114
  I've read articles that recommend no more than four digits before and two digits
@@ -38,15 +116,20 @@ def format_number(num: float | str) -> str:
38
116
  giving six. Mostly to eliminate exponential notation, but I'm "rstripping" the
39
117
  strings to reduce filesize and increase readability
40
118
 
41
- * reduce fp precision to 6 digits
119
+ * reduce fp precision to (default) 6 digits
42
120
  * remove trailing zeros
43
121
  * remove trailing decimal point
122
+ * remove leading 0 in "0.123"
44
123
  * convert "-0" to "0"
124
+ * use shorter of exponential or fixed-point notation
45
125
  """
46
- as_str = f"{float(num):0.6f}".rstrip("0").rstrip(".")
47
- if as_str == "-0":
48
- as_str = "0"
49
- return as_str
126
+ if precision is not None:
127
+ num = f"{float(num):.{precision}f}"
128
+ exponential_str = _format_as_exponential(num)
129
+ fixed_point_str = _format_as_fixed_point(num)
130
+ if len(exponential_str) < len(fixed_point_str):
131
+ return exponential_str
132
+ return fixed_point_str
50
133
 
51
134
 
52
135
  def format_numbers(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: svg-ultralight
3
- Version: 0.41.0
3
+ Version: 0.42.0
4
4
  Summary: a sensible way to create svg files with Python
5
5
  Author-email: Shay Hill <shay_public@hotmail.com>
6
6
  License: MIT
@@ -9,7 +9,7 @@ svg_ultralight/nsmap.py,sha256=y63upO78Rr-JJT56RWWZuyrsILh6HPoY4GhbYnK1A0g,1244
9
9
  svg_ultralight/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  svg_ultralight/query.py,sha256=iFFsK78TuUT6h3iv_V8_fwCggIAdYa97K7oOPhhoPuY,9711
11
11
  svg_ultralight/root_elements.py,sha256=E_H7HXk0M5F3IyFVOxO8PQmhww1-sHTzJhx8hBJPZvg,2911
12
- svg_ultralight/string_conversion.py,sha256=ZUehoKh1lzys1jqRDIhHWfpxi4TSUhNFHiQl57OCAr8,10324
12
+ svg_ultralight/string_conversion.py,sha256=U5IBhG05DtfSkJlbQ7BKHHbob78CHG6cF_Yr1EdB20U,13829
13
13
  svg_ultralight/transformations.py,sha256=T3vSxcTWOwWnwu3OF610LHMbKScUIVWICUAvru5zLnU,4488
14
14
  svg_ultralight/unit_conversion.py,sha256=g07nhzXdjPvGcJmkhLdFbeDLrSmbI8uFoVgPo7G62Bg,9258
15
15
  svg_ultralight/bounding_boxes/__init__.py,sha256=qUEn3r4s-1QNHaguhWhhaNfdP4tl_B6YEqxtiTFuzhQ,78
@@ -28,7 +28,7 @@ svg_ultralight/font_tools/font_info.py,sha256=00iNrJwWH2-de9CbaG1TqulRFmp_3aOyjR
28
28
  svg_ultralight/font_tools/globs.py,sha256=JdrrGMqDtD4WcY7YGUWV43DUW63RVev-x9vWqsQUhxU,119
29
29
  svg_ultralight/strings/__init__.py,sha256=BMGhF1pulscIgkiYvZLr6kPRR0L4lW0jUNFxkul4_EM,295
30
30
  svg_ultralight/strings/svg_strings.py,sha256=FQNxNmMkR2M-gCFo_woQKXLgCHi3ncUlRMiaRR_a9nQ,1978
31
- svg_ultralight-0.41.0.dist-info/METADATA,sha256=jJ9sHbqg4zJ3WcCuAMbiGb69FEtUcpZappC5LeZYl3Y,9022
32
- svg_ultralight-0.41.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
- svg_ultralight-0.41.0.dist-info/top_level.txt,sha256=se-6yqM_0Yg5orJKvKWdjQZ4iR4G_EjhL7oRgju-fdY,15
34
- svg_ultralight-0.41.0.dist-info/RECORD,,
31
+ svg_ultralight-0.42.0.dist-info/METADATA,sha256=Ey-v7B_Us35-JyL8y1p52yjkIZgqNT405c54lDcpGpU,9022
32
+ svg_ultralight-0.42.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
+ svg_ultralight-0.42.0.dist-info/top_level.txt,sha256=se-6yqM_0Yg5orJKvKWdjQZ4iR4G_EjhL7oRgju-fdY,15
34
+ svg_ultralight-0.42.0.dist-info/RECORD,,