consync 2.3.0__tar.gz → 2.3.1__tar.gz
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.
- {consync-2.3.0 → consync-2.3.1}/PKG-INFO +1 -1
- {consync-2.3.0 → consync-2.3.1}/consync/__init__.py +1 -1
- {consync-2.3.0 → consync-2.3.1}/consync/renderers/c_struct_table.py +74 -23
- {consync-2.3.0 → consync-2.3.1}/pyproject.toml +1 -1
- {consync-2.3.0 → consync-2.3.1}/tests/test_c_struct_table.py +120 -1
- {consync-2.3.0 → consync-2.3.1}/.github/CODEOWNERS +0 -0
- {consync-2.3.0 → consync-2.3.1}/.github/copilot-instructions.md +0 -0
- {consync-2.3.0 → consync-2.3.1}/.github/dependabot.yml +0 -0
- {consync-2.3.0 → consync-2.3.1}/.github/workflows/ci.yml +0 -0
- {consync-2.3.0 → consync-2.3.1}/.github/workflows/codeql.yml +0 -0
- {consync-2.3.0 → consync-2.3.1}/.github/workflows/publish.yml +0 -0
- {consync-2.3.0 → consync-2.3.1}/.github/workflows/release.yml +0 -0
- {consync-2.3.0 → consync-2.3.1}/.gitignore +0 -0
- {consync-2.3.0 → consync-2.3.1}/CLAUDE.md +0 -0
- {consync-2.3.0 → consync-2.3.1}/CONTRIBUTING.md +0 -0
- {consync-2.3.0 → consync-2.3.1}/FAQ.md +0 -0
- {consync-2.3.0 → consync-2.3.1}/LICENSE +0 -0
- {consync-2.3.0 → consync-2.3.1}/README.md +0 -0
- {consync-2.3.0 → consync-2.3.1}/SECURITY.md +0 -0
- {consync-2.3.0 → consync-2.3.1}/TODO.md +0 -0
- {consync-2.3.0 → consync-2.3.1}/assets/demo.gif +0 -0
- {consync-2.3.0 → consync-2.3.1}/assets/demo.tape +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/backup.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/cli.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/config.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/hooks.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/lock.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/logging_config.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/models.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/parsers/__init__.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/parsers/c_header.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/parsers/c_struct_table.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/parsers/csv_parser.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/parsers/json_parser.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/parsers/toml_parser.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/parsers/xlsx.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/precision.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/renderers/__init__.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/renderers/c_header.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/renderers/csharp.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/renderers/csv_renderer.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/renderers/json_renderer.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/renderers/python_const.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/renderers/rust_const.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/renderers/verilog.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/renderers/vhdl.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/state.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/sync.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/validators.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/consync/watcher.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/examples/fpga/.consync.yaml +0 -0
- {consync-2.3.0 → consync-2.3.1}/examples/fpga/design_params.csv +0 -0
- {consync-2.3.0 → consync-2.3.1}/examples/hardware/.consync.yaml +0 -0
- {consync-2.3.0 → consync-2.3.1}/examples/hardware/constants.csv +0 -0
- {consync-2.3.0 → consync-2.3.1}/examples/multilang/.consync.yaml +0 -0
- {consync-2.3.0 → consync-2.3.1}/examples/multilang/constants.json +0 -0
- {consync-2.3.0 → consync-2.3.1}/npm/.npmrc +0 -0
- {consync-2.3.0 → consync-2.3.1}/npm/LICENSE +0 -0
- {consync-2.3.0 → consync-2.3.1}/npm/README.md +0 -0
- {consync-2.3.0 → consync-2.3.1}/npm/bin/consync.js +0 -0
- {consync-2.3.0 → consync-2.3.1}/npm/package.json +0 -0
- {consync-2.3.0 → consync-2.3.1}/npm/scripts/install.js +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/__init__.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/test_arrays.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/test_audit_sync.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/test_bidirectional.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/test_cli.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/test_comprehensive_sync.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/test_embedded.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/test_parsers.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/test_precision.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/test_renderers.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/test_safety.py +0 -0
- {consync-2.3.0 → consync-2.3.1}/tests/test_sync.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: consync
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.1
|
|
4
4
|
Summary: Bidirectional sync between spreadsheets and source code constants — with full decimal precision.
|
|
5
5
|
Project-URL: Homepage, https://github.com/naveenkumarbaskaran/consync
|
|
6
6
|
Project-URL: Repository, https://github.com/naveenkumarbaskaran/consync
|
|
@@ -41,7 +41,12 @@ def _sanitize_label(label: str) -> str:
|
|
|
41
41
|
def _format_numeric(value: float | int, original_raw: str = "") -> str:
|
|
42
42
|
"""Format a numeric value back to C literal, preserving style of original.
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Preserves:
|
|
45
|
+
1. Original exponent for scientific notation (E-3 stays E-3)
|
|
46
|
+
2. Lowercase/uppercase 'e'/'E'
|
|
47
|
+
3. Mantissa format (with/without decimal point)
|
|
48
|
+
4. Precision (number of decimal digits)
|
|
49
|
+
5. Exponent digit count (E-03 vs E-3)
|
|
45
50
|
"""
|
|
46
51
|
if isinstance(value, int):
|
|
47
52
|
if original_raw and original_raw.strip().startswith(("0x", "0X")):
|
|
@@ -55,42 +60,88 @@ def _format_numeric(value: float | int, original_raw: str = "") -> str:
|
|
|
55
60
|
return f"{value}{suffix}"
|
|
56
61
|
|
|
57
62
|
# Float value
|
|
58
|
-
suffix = "F"
|
|
63
|
+
suffix = "F" # default
|
|
59
64
|
if original_raw:
|
|
60
|
-
stripped = original_raw.strip()
|
|
61
|
-
if
|
|
62
|
-
suffix =
|
|
65
|
+
stripped = original_raw.strip()
|
|
66
|
+
if stripped.endswith("f"):
|
|
67
|
+
suffix = "f"
|
|
68
|
+
elif stripped.endswith("F"):
|
|
69
|
+
suffix = "F"
|
|
70
|
+
elif stripped.endswith("L") or stripped.endswith("l"):
|
|
71
|
+
suffix = stripped[-1]
|
|
63
72
|
else:
|
|
64
73
|
suffix = ""
|
|
65
74
|
|
|
66
75
|
# Check if original used scientific notation
|
|
67
|
-
if original_raw and ("e" in original_raw
|
|
68
|
-
#
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
if original_raw and ("e" in original_raw or "E" in original_raw):
|
|
77
|
+
# Determine case of exponent character
|
|
78
|
+
use_lowercase = "e" in original_raw and "E" not in original_raw
|
|
79
|
+
exp_char = "e" if use_lowercase else "E"
|
|
80
|
+
|
|
81
|
+
# Extract original exponent value
|
|
82
|
+
orig_no_suffix = original_raw.strip().rstrip("fFlLuU")
|
|
83
|
+
exp_match = re.search(r"[eE]([+-]?\d+)", orig_no_suffix)
|
|
84
|
+
if exp_match:
|
|
85
|
+
orig_exponent = int(exp_match.group(1))
|
|
86
|
+
|
|
87
|
+
# Handle negative values
|
|
88
|
+
sign = ""
|
|
89
|
+
abs_value = value
|
|
90
|
+
if value < 0:
|
|
91
|
+
sign = "-"
|
|
92
|
+
abs_value = abs(value)
|
|
93
|
+
|
|
94
|
+
# Calculate new mantissa preserving the original exponent
|
|
95
|
+
# value = mantissa * 10^exponent => mantissa = value / 10^exponent
|
|
96
|
+
if abs_value == 0:
|
|
97
|
+
new_mantissa = 0.0
|
|
78
98
|
else:
|
|
79
|
-
|
|
80
|
-
else:
|
|
81
|
-
decimal_digits = 2
|
|
99
|
+
new_mantissa = abs_value / (10 ** orig_exponent)
|
|
82
100
|
|
|
83
|
-
|
|
84
|
-
|
|
101
|
+
# Determine mantissa format from original
|
|
102
|
+
mantissa_part = orig_no_suffix.split("e")[0].split("E")[0]
|
|
103
|
+
mantissa_part = mantissa_part.lstrip("+-")
|
|
104
|
+
|
|
105
|
+
if "." in mantissa_part:
|
|
106
|
+
decimal_digits = len(mantissa_part.split(".")[1])
|
|
107
|
+
mantissa_str = f"{new_mantissa:.{decimal_digits}f}"
|
|
108
|
+
else:
|
|
109
|
+
# No decimal in original (like "1E-2")
|
|
110
|
+
mantissa_str = f"{int(round(new_mantissa))}"
|
|
111
|
+
|
|
112
|
+
# Preserve exponent format (E-03 vs E-3, leading zeros)
|
|
113
|
+
exp_match_full = re.search(r"[eE]([+-]?)(\d+)", orig_no_suffix)
|
|
114
|
+
if exp_match_full:
|
|
115
|
+
exp_sign_char = exp_match_full.group(1)
|
|
116
|
+
exp_digit_count = len(exp_match_full.group(2))
|
|
117
|
+
if orig_exponent < 0:
|
|
118
|
+
exp_sign_str = "-"
|
|
119
|
+
elif exp_sign_char == "+":
|
|
120
|
+
exp_sign_str = "+"
|
|
121
|
+
else:
|
|
122
|
+
exp_sign_str = ""
|
|
123
|
+
exp_abs = abs(orig_exponent)
|
|
124
|
+
exp_str = f"{exp_sign_str}{exp_abs:0{exp_digit_count}d}"
|
|
125
|
+
else:
|
|
126
|
+
exp_str = f"{orig_exponent:+d}"
|
|
127
|
+
|
|
128
|
+
formatted = f"{sign}{mantissa_str}{exp_char}{exp_str}"
|
|
129
|
+
return f"{formatted}{suffix}"
|
|
130
|
+
else:
|
|
131
|
+
# Fallback (shouldn't happen if we got here)
|
|
132
|
+
formatted = f"{value:E}"
|
|
133
|
+
return f"{formatted}{suffix}"
|
|
85
134
|
else:
|
|
86
|
-
# Regular float notation
|
|
135
|
+
# Regular float notation (no scientific notation in original)
|
|
87
136
|
if original_raw:
|
|
88
137
|
orig_stripped = original_raw.strip().rstrip("fFlL")
|
|
89
138
|
if "." in orig_stripped:
|
|
90
139
|
decimal_digits = len(orig_stripped.split(".")[1])
|
|
91
140
|
formatted = f"{value:.{decimal_digits}f}"
|
|
92
141
|
else:
|
|
93
|
-
formatted =
|
|
142
|
+
formatted = (
|
|
143
|
+
str(int(round(value))) if value == int(value) else str(value)
|
|
144
|
+
)
|
|
94
145
|
else:
|
|
95
146
|
formatted = str(value)
|
|
96
147
|
return f"{formatted}{suffix}"
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "consync"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.1"
|
|
8
8
|
description = "Bidirectional sync between spreadsheets and source code constants — with full decimal precision."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
"""Tests for c_struct_table parser — generic C struct array initializer
|
|
1
|
+
"""Tests for c_struct_table parser and renderer — generic C struct array initializer."""
|
|
2
2
|
|
|
3
3
|
import textwrap
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
|
+
from consync.renderers.c_struct_table import _format_numeric
|
|
8
9
|
from consync.parsers.c_struct_table import (
|
|
9
10
|
parse_c_struct_table,
|
|
10
11
|
_auto_detect_table_var,
|
|
@@ -472,3 +473,121 @@ class TestParseCStructTable:
|
|
|
472
473
|
# Special chars in label → underscore, fields from header comment
|
|
473
474
|
assert result[0].name == "Motor_A_B_v2__alpha"
|
|
474
475
|
assert result[1].name == "Motor_A_B_v2__beta"
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
# ============================================================
|
|
479
|
+
# _format_numeric — renderer numeric formatting
|
|
480
|
+
# ============================================================
|
|
481
|
+
|
|
482
|
+
class TestFormatNumeric:
|
|
483
|
+
"""Tests for _format_numeric() in the c_struct_table renderer.
|
|
484
|
+
|
|
485
|
+
Covers all C literal format variations found in automotive embedded code:
|
|
486
|
+
scientific notation exponent preservation, case preservation, mantissa
|
|
487
|
+
format, precision, hex, unsigned, and regular floats.
|
|
488
|
+
"""
|
|
489
|
+
|
|
490
|
+
# --- Issue 1: Exponent preservation (CRITICAL) ---
|
|
491
|
+
|
|
492
|
+
def test_scientific_notation_preserves_exponent(self):
|
|
493
|
+
"""Original E-3 exponent must be preserved, not renormalized."""
|
|
494
|
+
assert _format_numeric(0.205, "195.0E-3F") == "205.0E-3F"
|
|
495
|
+
|
|
496
|
+
def test_scientific_notation_same_value(self):
|
|
497
|
+
assert _format_numeric(0.195, "195.0E-3F") == "195.0E-3F"
|
|
498
|
+
|
|
499
|
+
def test_scientific_notation_higher_precision(self):
|
|
500
|
+
assert _format_numeric(0.0315, "30.13E-3F") == "31.50E-3F"
|
|
501
|
+
|
|
502
|
+
def test_scientific_notation_micro_scale(self):
|
|
503
|
+
assert _format_numeric(0.00002521, "24.21E-6F") == "25.21E-6F"
|
|
504
|
+
|
|
505
|
+
def test_scientific_notation_micro_scale_same(self):
|
|
506
|
+
assert _format_numeric(0.00002421, "24.21E-6F") == "24.21E-6F"
|
|
507
|
+
|
|
508
|
+
def test_scientific_notation_negative_value(self):
|
|
509
|
+
assert _format_numeric(-0.0001242, "-1.242E-4F") == "-1.242E-4F"
|
|
510
|
+
|
|
511
|
+
def test_scientific_notation_negative_different_exp(self):
|
|
512
|
+
assert _format_numeric(-0.0000101, "-1.01E-5F") == "-1.01E-5F"
|
|
513
|
+
|
|
514
|
+
def test_scientific_notation_positive_exponent(self):
|
|
515
|
+
assert _format_numeric(1000.0, "1.0E3F") == "1.0E3F"
|
|
516
|
+
|
|
517
|
+
def test_scientific_notation_positive_exponent_changed(self):
|
|
518
|
+
assert _format_numeric(2000.0, "1.0E3F") == "2.0E3F"
|
|
519
|
+
|
|
520
|
+
def test_scientific_notation_milli_scale(self):
|
|
521
|
+
assert _format_numeric(0.052, "42.0E-3F") == "52.0E-3F"
|
|
522
|
+
|
|
523
|
+
# --- Issue 2: Lowercase 'e' preservation ---
|
|
524
|
+
|
|
525
|
+
def test_lowercase_e_preserved_same_value(self):
|
|
526
|
+
assert _format_numeric(0.0726, "72.6e-3F") == "72.6e-3F"
|
|
527
|
+
|
|
528
|
+
def test_lowercase_e_preserved_new_value(self):
|
|
529
|
+
assert _format_numeric(0.0831, "72.6e-3F") == "83.1e-3F"
|
|
530
|
+
|
|
531
|
+
# --- Issue 3: No decimal point in mantissa ---
|
|
532
|
+
|
|
533
|
+
def test_no_decimal_mantissa_same(self):
|
|
534
|
+
assert _format_numeric(0.01, "1E-2F") == "1E-2F"
|
|
535
|
+
|
|
536
|
+
def test_no_decimal_mantissa_4e3(self):
|
|
537
|
+
assert _format_numeric(0.004, "4E-3F") == "4E-3F"
|
|
538
|
+
|
|
539
|
+
def test_no_decimal_mantissa_6e3(self):
|
|
540
|
+
assert _format_numeric(0.006, "6E-3F") == "6E-3F"
|
|
541
|
+
|
|
542
|
+
def test_no_decimal_mantissa_changed(self):
|
|
543
|
+
assert _format_numeric(0.02, "1E-2F") == "2E-2F"
|
|
544
|
+
|
|
545
|
+
# --- Issue 4: High precision + exponent digit preservation ---
|
|
546
|
+
|
|
547
|
+
def test_high_precision_same(self):
|
|
548
|
+
assert _format_numeric(0.002457019940, "2.457019940E-03F") == "2.457019940E-03F"
|
|
549
|
+
|
|
550
|
+
def test_high_precision_changed(self):
|
|
551
|
+
assert _format_numeric(0.002557019940, "2.457019940E-03F") == "2.557019940E-03F"
|
|
552
|
+
|
|
553
|
+
# --- Working cases: integers ---
|
|
554
|
+
|
|
555
|
+
def test_unsigned_int_same(self):
|
|
556
|
+
assert _format_numeric(5, "5u") == "5u"
|
|
557
|
+
|
|
558
|
+
def test_unsigned_int_changed(self):
|
|
559
|
+
assert _format_numeric(7, "5u") == "7u"
|
|
560
|
+
|
|
561
|
+
def test_hex_literal_same(self):
|
|
562
|
+
assert _format_numeric(255, "0xFF") == "0xFF"
|
|
563
|
+
|
|
564
|
+
def test_hex_literal_changed(self):
|
|
565
|
+
assert _format_numeric(16, "0xFF") == "0x10"
|
|
566
|
+
|
|
567
|
+
# --- Working cases: regular floats ---
|
|
568
|
+
|
|
569
|
+
def test_regular_float_same(self):
|
|
570
|
+
assert _format_numeric(121.0, "121.0F") == "121.0F"
|
|
571
|
+
|
|
572
|
+
def test_regular_float_changed(self):
|
|
573
|
+
assert _format_numeric(125.5, "121.0F") == "125.5F"
|
|
574
|
+
|
|
575
|
+
def test_small_float_no_sci(self):
|
|
576
|
+
assert _format_numeric(0.0055, "0.0055F") == "0.0055F"
|
|
577
|
+
|
|
578
|
+
def test_float_three_decimals(self):
|
|
579
|
+
assert _format_numeric(0.087, "0.087F") == "0.087F"
|
|
580
|
+
|
|
581
|
+
def test_zero_regular(self):
|
|
582
|
+
assert _format_numeric(0.0, "0.0F") == "0.0F"
|
|
583
|
+
|
|
584
|
+
def test_zero_scientific(self):
|
|
585
|
+
assert _format_numeric(0.0, "0.0E-3F") == "0.0E-3F"
|
|
586
|
+
|
|
587
|
+
def test_no_original_raw(self):
|
|
588
|
+
"""When no original is provided, just format the value."""
|
|
589
|
+
result = _format_numeric(3.14)
|
|
590
|
+
assert result == "3.14F"
|
|
591
|
+
|
|
592
|
+
def test_int_no_original(self):
|
|
593
|
+
assert _format_numeric(42) == "42"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|