consync 0.1.0__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.
Files changed (61) hide show
  1. consync-0.1.0/.github/CODEOWNERS +2 -0
  2. consync-0.1.0/.github/copilot-instructions.md +94 -0
  3. consync-0.1.0/.github/dependabot.yml +18 -0
  4. consync-0.1.0/.github/workflows/ci.yml +40 -0
  5. consync-0.1.0/.github/workflows/codeql.yml +40 -0
  6. consync-0.1.0/.github/workflows/publish.yml +32 -0
  7. consync-0.1.0/.gitignore +19 -0
  8. consync-0.1.0/CLAUDE.md +132 -0
  9. consync-0.1.0/CONTRIBUTING.md +89 -0
  10. consync-0.1.0/FAQ.md +280 -0
  11. consync-0.1.0/LICENSE +21 -0
  12. consync-0.1.0/PKG-INFO +590 -0
  13. consync-0.1.0/README.md +555 -0
  14. consync-0.1.0/SECURITY.md +41 -0
  15. consync-0.1.0/TODO.md +124 -0
  16. consync-0.1.0/assets/demo.gif +0 -0
  17. consync-0.1.0/assets/demo.tape +69 -0
  18. consync-0.1.0/consync/__init__.py +9 -0
  19. consync-0.1.0/consync/backup.py +188 -0
  20. consync-0.1.0/consync/cli.py +372 -0
  21. consync-0.1.0/consync/config.py +200 -0
  22. consync-0.1.0/consync/hooks.py +81 -0
  23. consync-0.1.0/consync/lock.py +118 -0
  24. consync-0.1.0/consync/logging_config.py +273 -0
  25. consync-0.1.0/consync/models.py +104 -0
  26. consync-0.1.0/consync/parsers/__init__.py +40 -0
  27. consync-0.1.0/consync/parsers/c_header.py +96 -0
  28. consync-0.1.0/consync/parsers/csv_parser.py +133 -0
  29. consync-0.1.0/consync/parsers/json_parser.py +138 -0
  30. consync-0.1.0/consync/parsers/toml_parser.py +74 -0
  31. consync-0.1.0/consync/parsers/xlsx.py +116 -0
  32. consync-0.1.0/consync/precision.py +148 -0
  33. consync-0.1.0/consync/renderers/__init__.py +49 -0
  34. consync-0.1.0/consync/renderers/c_header.py +222 -0
  35. consync-0.1.0/consync/renderers/csharp.py +174 -0
  36. consync-0.1.0/consync/renderers/csv_renderer.py +46 -0
  37. consync-0.1.0/consync/renderers/json_renderer.py +71 -0
  38. consync-0.1.0/consync/renderers/python_const.py +84 -0
  39. consync-0.1.0/consync/renderers/rust_const.py +90 -0
  40. consync-0.1.0/consync/renderers/verilog.py +89 -0
  41. consync-0.1.0/consync/renderers/vhdl.py +94 -0
  42. consync-0.1.0/consync/state.py +76 -0
  43. consync-0.1.0/consync/sync.py +458 -0
  44. consync-0.1.0/consync/validators.py +233 -0
  45. consync-0.1.0/consync/watcher.py +176 -0
  46. consync-0.1.0/examples/fpga/.consync.yaml +17 -0
  47. consync-0.1.0/examples/fpga/design_params.csv +11 -0
  48. consync-0.1.0/examples/hardware/.consync.yaml +11 -0
  49. consync-0.1.0/examples/hardware/constants.csv +8 -0
  50. consync-0.1.0/examples/multilang/.consync.yaml +21 -0
  51. consync-0.1.0/examples/multilang/constants.json +7 -0
  52. consync-0.1.0/pyproject.toml +54 -0
  53. consync-0.1.0/tests/__init__.py +0 -0
  54. consync-0.1.0/tests/test_arrays.py +469 -0
  55. consync-0.1.0/tests/test_cli.py +94 -0
  56. consync-0.1.0/tests/test_embedded.py +271 -0
  57. consync-0.1.0/tests/test_parsers.py +148 -0
  58. consync-0.1.0/tests/test_precision.py +129 -0
  59. consync-0.1.0/tests/test_renderers.py +210 -0
  60. consync-0.1.0/tests/test_safety.py +511 -0
  61. consync-0.1.0/tests/test_sync.py +152 -0
@@ -0,0 +1,2 @@
1
+ # Default owner for everything in the repo
2
+ * @naveenkumarbaskaran
@@ -0,0 +1,94 @@
1
+ # Copilot Instructions for consync
2
+
3
+ ## What is consync?
4
+
5
+ A Python CLI that bidirectionally syncs constants between spreadsheets (xlsx/csv/json/toml)
6
+ and source code (C, C#, Python, Rust, Verilog, VHDL). Built for embedded/firmware engineers
7
+ who need full IEEE 754 precision (17 significant digits).
8
+
9
+ ## Project Structure
10
+
11
+ ```
12
+ consync/
13
+ ├── cli.py # Click CLI — init, sync, check, watch, diff, recover, log, status
14
+ ├── sync.py # Core engine — direction detection, backup, validate, render
15
+ ├── config.py # .consync.yaml loader + format auto-detect
16
+ ├── models.py # Constant, MappingConfig, ConsyncConfig dataclasses
17
+ ├── state.py # SyncState (MD5 hashes for change detection)
18
+ ├── watcher.py # File watcher — queues events, retries on lock, startup sync
19
+ ├── hooks.py # Git pre-commit hook installer
20
+ ├── backup.py # Timestamped snapshots + recovery
21
+ ├── lock.py # Advisory .consync.lock with stale-PID detection
22
+ ├── validators.py # Range/type/pattern/length checks
23
+ ├── logging_config.py # 3-layer: console, rotating file, audit JSONL
24
+ ├── parsers/ # xlsx, csv, json, toml, c_header
25
+ └── renderers/ # c_header, csharp, python, rust, verilog, vhdl, json, csv
26
+ tests/
27
+ ├── test_parsers.py # Parser unit tests
28
+ ├── test_renderers.py # Renderer unit tests
29
+ ├── test_sync.py # Sync engine + state tests
30
+ ├── test_arrays.py # Array constant support tests
31
+ ├── test_safety.py # Backup, recover, validation, lock tests
32
+ └── ...
33
+ ```
34
+
35
+ ## Key Patterns
36
+
37
+ - **Universal model**: `Constant(name: str, value: int|float|str|list, unit: str, description: str)`
38
+ - **Every format is a parser AND/OR renderer** — enables bootstrap and round-trip
39
+ - **Safety-first for firmware**: backup before write, validate before render, lock during sync
40
+ - **Watcher queues events** — never drops changes, retries on lock conflict
41
+ - **Audit trail**: JSONL log with every constant value for traceability
42
+
43
+ ## When Editing Code
44
+
45
+ 1. **Adding a parser/renderer**: Register in `__init__.py` of the package + add to `EXTENSION_TO_FORMAT` in `config.py`
46
+ 2. **Adding CLI command**: Use `@main.command()` in `cli.py`, lazy imports inside function
47
+ 3. **Modifying sync behavior**: Ensure backup + validation + lock flow is preserved
48
+ 4. **Running tests**: `pytest tests/ -v` (151+ tests, takes ~0.5s)
49
+
50
+ ## Constraints
51
+
52
+ - Python >=3.10
53
+ - Minimal deps: openpyxl, click, watchdog, pyyaml
54
+ - Precision: 17 significant digits default (IEEE 754 round-trip fidelity)
55
+ - Never drop file changes in watcher (queue + coalesce)
56
+ - Lock must be held during all write operations
57
+ - Backup must exist before overwriting any file
58
+
59
+ ## Config Example (.consync.yaml)
60
+
61
+ ```yaml
62
+ mappings:
63
+ - source: calibration.xlsx
64
+ target: firmware/config.h
65
+ direction: source_to_target
66
+ precision: 17
67
+ header_guard: CONFIG_H
68
+ static_const: true
69
+ typed_ints: true
70
+ validators:
71
+ BRAKE_PRESSURE:
72
+ min: 0
73
+ max: 300
74
+ TIMEOUT_MS:
75
+ type: int
76
+ min: 100
77
+ ```
78
+
79
+ ## CLI Command Reference
80
+
81
+ | Command | Purpose |
82
+ |---------|---------|
83
+ | `consync init` | Create .consync.yaml template |
84
+ | `consync sync` | Sync all mappings |
85
+ | `consync sync --dry-run` | Show what would be synced |
86
+ | `consync sync --from source` | Force direction |
87
+ | `consync check` | CI gate — exit 1 if out of sync |
88
+ | `consync watch` | File watcher with auto-sync |
89
+ | `consync diff` | Unified diff preview |
90
+ | `consync recover --list` | List backup snapshots |
91
+ | `consync recover --file X --last` | Restore most recent backup |
92
+ | `consync log` | Show audit trail with values |
93
+ | `consync install-hook` | Install git pre-commit hook |
94
+ | `consync status` | Show sync state per mapping |
@@ -0,0 +1,18 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "monthly"
7
+ groups:
8
+ all:
9
+ patterns:
10
+ - "*"
11
+ - package-ecosystem: "github-actions"
12
+ directory: "/"
13
+ schedule:
14
+ interval: "monthly"
15
+ groups:
16
+ github-actions:
17
+ patterns:
18
+ - "*"
@@ -0,0 +1,40 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install dependencies
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ pip install -e ".[dev]"
28
+
29
+ - name: Lint with ruff
30
+ run: ruff check consync/ tests/
31
+
32
+ - name: Run tests
33
+ run: pytest --cov=consync --cov-report=xml -v
34
+
35
+ - name: Upload coverage
36
+ if: matrix.python-version == '3.12'
37
+ uses: codecov/codecov-action@v4
38
+ with:
39
+ file: ./coverage.xml
40
+ continue-on-error: true
@@ -0,0 +1,40 @@
1
+ name: "CodeQL"
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ schedule:
9
+ - cron: "0 6 * * 1" # Every Monday at 06:00 UTC
10
+
11
+ jobs:
12
+ analyze:
13
+ name: Analyze
14
+ runs-on: ubuntu-latest
15
+ permissions:
16
+ actions: read
17
+ contents: read
18
+ security-events: write
19
+
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ language: [python]
24
+
25
+ steps:
26
+ - name: Checkout repository
27
+ uses: actions/checkout@v4
28
+
29
+ - name: Initialize CodeQL
30
+ uses: github/codeql-action/init@v3
31
+ with:
32
+ languages: ${{ matrix.language }}
33
+
34
+ - name: Autobuild
35
+ uses: github/codeql-action/autobuild@v3
36
+
37
+ - name: Perform CodeQL Analysis
38
+ uses: github/codeql-action/analyze@v3
39
+ with:
40
+ category: "/language:${{ matrix.language }}"
@@ -0,0 +1,32 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ permissions:
15
+ id-token: write # Required for trusted publishing
16
+
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.12"
24
+
25
+ - name: Install build tools
26
+ run: pip install build
27
+
28
+ - name: Build package
29
+ run: python -m build
30
+
31
+ - name: Publish to PyPI
32
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,19 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .coverage
8
+ .sync_state.json
9
+ *.xlsx
10
+ !examples/**/*.xlsx
11
+ .consync.state.json
12
+
13
+ # consync runtime files
14
+ .consync.log
15
+ .consync.log.*
16
+ .consync.audit.jsonl
17
+ .consync.state.json
18
+ .consync.lock
19
+ .consync/
@@ -0,0 +1,132 @@
1
+ # consync — AI Assistant Instructions (CLAUDE.md)
2
+
3
+ ## Project Overview
4
+
5
+ **consync** is a pip-installable Python CLI for bidirectional synchronisation between
6
+ spreadsheets (xlsx/csv/json/toml) and source code constant declarations (C, C#, Python,
7
+ Rust, Verilog, VHDL). It preserves full IEEE 754 precision (17 significant digits) and
8
+ supports embedded/firmware workflows.
9
+
10
+ **Repo:** https://github.com/naveenkumarbaskaran/consync
11
+ **Author:** Naveen Kumar Baskaran (naveenkb142@gmail.com)
12
+ **Python:** >=3.10
13
+ **Build:** hatchling
14
+ **CLI:** Click
15
+
16
+ ---
17
+
18
+ ## Architecture
19
+
20
+ ```
21
+ .consync.yaml → Config Loader → Sync Engine → State Tracker (.consync.state.json)
22
+ ↓ ↓
23
+ ┌──────────────┐ ┌──────────────┐
24
+ │ Parsers │ │ Renderers │
25
+ │ xlsx csv json│ │ c_header csv │
26
+ │ toml c_header│ │ csharp python│
27
+ └──────────────┘ │ rust verilog │
28
+ │ vhdl json │
29
+ └──────────────┘
30
+ ```
31
+
32
+ **Key design:** Every format can be both parser AND renderer. This enables bootstrap
33
+ (create spreadsheet from existing code) and round-trip (code → spreadsheet → code).
34
+
35
+ ---
36
+
37
+ ## Module Map
38
+
39
+ | File | Purpose |
40
+ |------|---------|
41
+ | `consync/cli.py` | Click CLI entry point — all commands |
42
+ | `consync/sync.py` | Core sync engine — direction detection, orchestration |
43
+ | `consync/config.py` | YAML config loader, format auto-detection |
44
+ | `consync/models.py` | `Constant`, `MappingConfig`, `ConsyncConfig` dataclasses |
45
+ | `consync/state.py` | `SyncState` — MD5 hashes per-mapping for change detection |
46
+ | `consync/watcher.py` | File watcher with debounce, queue, lock-retry |
47
+ | `consync/hooks.py` | Git hook installer |
48
+ | `consync/backup.py` | Auto-snapshot before every write + recovery |
49
+ | `consync/lock.py` | Advisory `.consync.lock` with stale-PID detection |
50
+ | `consync/validators.py` | User-defined value constraints (range/type/pattern) |
51
+ | `consync/logging_config.py` | 3-layer logging (console, file, audit JSONL) |
52
+ | `consync/parsers/` | One module per input format |
53
+ | `consync/renderers/` | One module per output format |
54
+
55
+ ---
56
+
57
+ ## Conventions
58
+
59
+ - **Dataclass-centric**: All data flows through `Constant(name, value, unit, description, metadata)`
60
+ - **No global state**: Config/state passed explicitly; watcher is the only stateful entry point
61
+ - **Format string IDs**: `"xlsx"`, `"csv"`, `"c_header"`, `"csharp"`, `"python"`, `"rust"`, `"verilog"`, `"vhdl"`, `"json"`, `"toml"`
62
+ - **Tests**: pytest, grouped by feature in `tests/`. Run with `pytest tests/ -v`
63
+ - **Logging**: Use `logger = logging.getLogger(__name__)` in every module. Audit via `write_audit_entry()`
64
+
65
+ ---
66
+
67
+ ## Adding a New Parser
68
+
69
+ 1. Create `consync/parsers/my_format.py` with function `parse(filepath: Path) -> list[Constant]`
70
+ 2. Register in `consync/parsers/__init__.py` → `PARSERS` dict
71
+ 3. Add extension mapping in `consync/config.py` → `EXTENSION_TO_FORMAT`
72
+ 4. Add tests in `tests/test_parsers.py`
73
+
74
+ ## Adding a New Renderer
75
+
76
+ 1. Create `consync/renderers/my_format.py` with function `render(constants: list[Constant], filepath: Path, config: MappingConfig)`
77
+ 2. Register in `consync/renderers/__init__.py` → `RENDERERS` dict
78
+ 3. Add extension mapping in `consync/config.py` → `EXTENSION_TO_FORMAT`
79
+ 4. Add tests in `tests/test_renderers.py`
80
+
81
+ ---
82
+
83
+ ## Safety Features (Critical for Embedded)
84
+
85
+ | Feature | Module | Behaviour |
86
+ |---------|--------|-----------|
87
+ | **Backup** | `backup.py` | Copies target to `.consync/backups/` before every write. Retains 20/file. |
88
+ | **Recovery** | `backup.py` + CLI `recover` | Restore any file by timestamp. Creates safety backup before restoring. |
89
+ | **Validation** | `validators.py` | Blocks sync if values violate `validators:` rules in config. |
90
+ | **Lock** | `lock.py` | Advisory PID-based lock prevents concurrent writes. Auto-reclaims stale. |
91
+ | **Audit** | `logging_config.py` | Every sync logged with timestamp, user, direction, all values. |
92
+ | **Queue** | `watcher.py` | Events during debounce are queued (never dropped). Lock conflicts retried 3x. |
93
+ | **Startup sync** | `watcher.py` | On `consync watch`, a full sync runs first to recover any drift. |
94
+
95
+ ---
96
+
97
+ ## Common Tasks
98
+
99
+ ### Run tests
100
+ ```bash
101
+ cd /path/to/consync
102
+ pip install -e ".[dev]"
103
+ pytest tests/ -v
104
+ ```
105
+
106
+ ### Run a specific test file
107
+ ```bash
108
+ pytest tests/test_safety.py -v
109
+ ```
110
+
111
+ ### Test the CLI manually
112
+ ```bash
113
+ cd examples/
114
+ consync sync --dry-run
115
+ consync diff
116
+ consync log -n 5
117
+ consync recover --list
118
+ ```
119
+
120
+ ### Add a new CLI command
121
+ 1. Add function with `@main.command()` decorator in `cli.py`
122
+ 2. Follow pattern: import lazily inside function, handle errors, call `sys.exit(1)` on failure
123
+
124
+ ---
125
+
126
+ ## Do NOT
127
+
128
+ - Put secrets/credentials in any file (there are none in this project)
129
+ - Modify `pyproject.toml` build config without testing `pip install -e .`
130
+ - Break round-trip precision (17 sig digits must survive parse→render→parse)
131
+ - Remove the lock/backup from sync.py — these are safety-critical for firmware engineers
132
+ - Add heavy dependencies (keep it lightweight: openpyxl, click, watchdog, pyyaml only)
@@ -0,0 +1,89 @@
1
+ # Contributing to consync
2
+
3
+ Thanks for your interest in contributing! Here's how to get started.
4
+
5
+ ## Development Setup
6
+
7
+ ```bash
8
+ git clone https://github.com/naveenkumarbaskaran/consync.git
9
+ cd consync
10
+ python -m venv .venv
11
+ source .venv/bin/activate
12
+ pip install -e ".[dev]"
13
+ ```
14
+
15
+ ## Running Tests
16
+
17
+ ```bash
18
+ pytest
19
+ ```
20
+
21
+ All 77+ tests should pass. Tests cover precision, parsers, renderers, sync engine, and CLI.
22
+
23
+ ## Code Style
24
+
25
+ This project uses [ruff](https://docs.astral.sh/ruff/) for linting and formatting:
26
+
27
+ ```bash
28
+ ruff check .
29
+ ruff format .
30
+ ```
31
+
32
+ ## Making Changes
33
+
34
+ 1. Fork the repo and create a feature branch from `main`
35
+ 2. Make your changes with clear, descriptive commits
36
+ 3. Add or update tests for any new functionality
37
+ 4. Ensure all tests pass and linting is clean
38
+ 5. Open a pull request against `main`
39
+
40
+ ## Commit Messages
41
+
42
+ Follow [Conventional Commits](https://www.conventionalcommits.org/):
43
+
44
+ - `feat: add TOML renderer for constants output`
45
+ - `fix: handle hex values in C header parser`
46
+ - `test: add round-trip precision tests for verilog`
47
+ - `docs: update CLI reference for --from flag`
48
+
49
+ ## Architecture Overview
50
+
51
+ ```
52
+ Source (xlsx/csv/json/toml) ←→ [consync engine] ←→ Target (C/Python/Rust/Verilog/VHDL)
53
+ ```
54
+
55
+ ### Key Modules
56
+
57
+ - `consync/parsers/` — Read constants from various file formats
58
+ - `consync/renderers/` — Write constants to target languages
59
+ - `consync/sync.py` — Core sync engine (direction detection, state management)
60
+ - `consync/precision.py` — IEEE 754 precision formatting
61
+ - `consync/cli.py` — Click-based CLI entry point
62
+ - `consync/watcher.py` — File watcher for continuous sync
63
+ - `consync/state.py` — Hash-based change detection
64
+
65
+ ## Adding a New Parser
66
+
67
+ 1. Create `consync/parsers/your_format.py`
68
+ 2. Implement a function that returns `list[Constant]`
69
+ 3. Decorate with `@register("format_name")`
70
+ 4. Import in `consync/parsers/__init__.py`
71
+ 5. Add tests in `tests/test_parsers.py`
72
+
73
+ ## Adding a New Renderer
74
+
75
+ 1. Create `consync/renderers/your_format.py`
76
+ 2. Implement a function that writes constants to a file
77
+ 3. Decorate with `@register("format_name")`
78
+ 4. Import in `consync/renderers/__init__.py`
79
+ 5. Add tests in `tests/test_renderers.py`
80
+
81
+ ## Reporting Issues
82
+
83
+ - Use GitHub Issues with a clear title and reproduction steps
84
+ - Include your Python version, OS, and error output
85
+ - For sync issues, include your `.consync.yaml` (redact any sensitive paths)
86
+
87
+ ## License
88
+
89
+ By contributing, you agree that your contributions will be licensed under the MIT License.