ihexer 0.0.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.
- ihexer-0.0.0/LICENSE +22 -0
- ihexer-0.0.0/PKG-INFO +128 -0
- ihexer-0.0.0/README.md +99 -0
- ihexer-0.0.0/pyproject.toml +160 -0
- ihexer-0.0.0/src/ihexer/__init__.py +1 -0
- ihexer-0.0.0/src/ihexer/_run.py +12 -0
- ihexer-0.0.0/src/ihexer/ihexer.py +193 -0
- ihexer-0.0.0/src/ihexer/main.py +80 -0
- ihexer-0.0.0/src/ihexer/py.typed +0 -0
ihexer-0.0.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
MIT License
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2025 Avengineers
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
in the Software without restriction, including without limitation the rights
|
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
furnished to do so, subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
SOFTWARE.
|
ihexer-0.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: ihexer
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Parse, view and diff files in Intel HEX format.
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Avengineers
|
|
7
|
+
Author-email: karsten.guenther@kamg.de
|
|
8
|
+
Requires-Python: >=3.10,<3.14
|
|
9
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
20
|
+
Requires-Dist: intelhex (>=2.3,<3.0)
|
|
21
|
+
Requires-Dist: py-app-dev (>=2.1.0,<3.0.0)
|
|
22
|
+
Requires-Dist: typer (>=0.12.3,<0.13.0)
|
|
23
|
+
Project-URL: Bug Tracker, https://github.com/avengineers/ihexer/issues
|
|
24
|
+
Project-URL: Changelog, https://github.com/avengineers/ihexer/blob/develop/CHANGELOG.md
|
|
25
|
+
Project-URL: Documentation, https://ihexer.readthedocs.io
|
|
26
|
+
Project-URL: Repository, https://github.com/avengineers/ihexer
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# Intel HEX Tools
|
|
30
|
+
|
|
31
|
+
<p align="center">
|
|
32
|
+
<a href="https://github.com/avengineers/ihexer/actions/workflows/ci.yml?query=branch%3Adevelop">
|
|
33
|
+
<img src="https://img.shields.io/github/actions/workflow/status/avengineers/ihexer/ci.yml?branch=develop&label=CI&logo=github&style=flat-square" alt="CI Status" >
|
|
34
|
+
</a>
|
|
35
|
+
<a href="https://ihexer.readthedocs.io">
|
|
36
|
+
<img src="https://img.shields.io/readthedocs/ihexer.svg?logo=read-the-docs&logoColor=fff&style=flat-square" alt="Documentation Status">
|
|
37
|
+
</a>
|
|
38
|
+
<a href="https://codecov.io/gh/avengineers/ihexer">
|
|
39
|
+
<img src="https://img.shields.io/codecov/c/github/avengineers/ihexer.svg?logo=codecov&logoColor=fff&style=flat-square" alt="Test coverage percentage">
|
|
40
|
+
</a>
|
|
41
|
+
</p>
|
|
42
|
+
<p align="center">
|
|
43
|
+
<a href="https://python-poetry.org/">
|
|
44
|
+
<img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">
|
|
45
|
+
</a>
|
|
46
|
+
<a href="https://github.com/astral-sh/ruff">
|
|
47
|
+
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="ruff">
|
|
48
|
+
</a>
|
|
49
|
+
<a href="https://github.com/pre-commit/pre-commit">
|
|
50
|
+
<img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">
|
|
51
|
+
</a>
|
|
52
|
+
</p>
|
|
53
|
+
<p align="center">
|
|
54
|
+
<a href="https://pypi.org/project/ihexer/">
|
|
55
|
+
<img src="https://img.shields.io/pypi/v/ihexer.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
|
|
56
|
+
</a>
|
|
57
|
+
<img src="https://img.shields.io/pypi/pyversions/ihexer.svg?style=flat-square&logo=python&logoColor=fff" alt="Supported Python versions">
|
|
58
|
+
<img src="https://img.shields.io/pypi/l/ihexer.svg?style=flat-square" alt="License">
|
|
59
|
+
</p>
|
|
60
|
+
|
|
61
|
+
Parse, view and diff files in Intel HEX format.
|
|
62
|
+
|
|
63
|
+
## Installation
|
|
64
|
+
|
|
65
|
+
Install this via pip (or your favourite package manager):
|
|
66
|
+
|
|
67
|
+
`pip install ihexer`
|
|
68
|
+
|
|
69
|
+
## Start developing
|
|
70
|
+
|
|
71
|
+
The project uses Poetry for dependencies management and packaging.
|
|
72
|
+
Run the `bootstrap.ps1` script to install Python and create the virtual environment. (This is only working on windows machines!)
|
|
73
|
+
|
|
74
|
+
```powershell
|
|
75
|
+
.\bootstrap.ps1
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This will also generate a `poetry.lock` file, you should track this file in version control.
|
|
79
|
+
|
|
80
|
+
If you want to customize the bootstrapping process please create a bootstrap.json file according to the [bootstrap documentation](https://github.com/avengineers/bootstrap?tab=readme-ov-file#configuration).
|
|
81
|
+
|
|
82
|
+
To execute the test suite, call pytest inside Poetry's virtual environment via `poetry run`:
|
|
83
|
+
|
|
84
|
+
```shell
|
|
85
|
+
.venv/Scripts/poetry run pytest
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Check out the Poetry documentation for more information on the available commands.
|
|
89
|
+
|
|
90
|
+
For those using [VS Code](https://code.visualstudio.com/) there are tasks defined for the most common commands:
|
|
91
|
+
|
|
92
|
+
- bootstrap
|
|
93
|
+
- install dependencies
|
|
94
|
+
- run tests
|
|
95
|
+
- run all checks configured for pre-commit
|
|
96
|
+
- generate documentation
|
|
97
|
+
|
|
98
|
+
See the `.vscode/tasks.json` for more details.
|
|
99
|
+
|
|
100
|
+
## Committing changes
|
|
101
|
+
|
|
102
|
+
This repository uses [commitlint](https://github.com/conventional-changelog/commitlint) for checking if the commit message meets the [conventional commit format](https://www.conventionalcommits.org/en).
|
|
103
|
+
|
|
104
|
+
## Release
|
|
105
|
+
|
|
106
|
+
This repository uses [semantic release](https://python-semantic-release.readthedocs.io/en/latest/) to automate versioning the Python projects.
|
|
107
|
+
The package version will be automatically updated when the `develop` branch is built.
|
|
108
|
+
|
|
109
|
+
## Contributors ✨
|
|
110
|
+
|
|
111
|
+
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
112
|
+
|
|
113
|
+
<!-- prettier-ignore-start -->
|
|
114
|
+
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
|
115
|
+
<!-- markdownlint-disable -->
|
|
116
|
+
<!-- markdownlint-enable -->
|
|
117
|
+
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
118
|
+
<!-- prettier-ignore-end -->
|
|
119
|
+
|
|
120
|
+
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
|
121
|
+
|
|
122
|
+
## Credits
|
|
123
|
+
|
|
124
|
+
This package was created with
|
|
125
|
+
[Copier](https://copier.readthedocs.io/) and the
|
|
126
|
+
[cuinixam/pypackage-template](https://github.com/cuinixam/pypackage-template)
|
|
127
|
+
project template.
|
|
128
|
+
|
ihexer-0.0.0/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Intel HEX Tools
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://github.com/avengineers/ihexer/actions/workflows/ci.yml?query=branch%3Adevelop">
|
|
5
|
+
<img src="https://img.shields.io/github/actions/workflow/status/avengineers/ihexer/ci.yml?branch=develop&label=CI&logo=github&style=flat-square" alt="CI Status" >
|
|
6
|
+
</a>
|
|
7
|
+
<a href="https://ihexer.readthedocs.io">
|
|
8
|
+
<img src="https://img.shields.io/readthedocs/ihexer.svg?logo=read-the-docs&logoColor=fff&style=flat-square" alt="Documentation Status">
|
|
9
|
+
</a>
|
|
10
|
+
<a href="https://codecov.io/gh/avengineers/ihexer">
|
|
11
|
+
<img src="https://img.shields.io/codecov/c/github/avengineers/ihexer.svg?logo=codecov&logoColor=fff&style=flat-square" alt="Test coverage percentage">
|
|
12
|
+
</a>
|
|
13
|
+
</p>
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://python-poetry.org/">
|
|
16
|
+
<img src="https://img.shields.io/badge/packaging-poetry-299bd7?style=flat-square&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAASCAYAAABrXO8xAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJJSURBVHgBfZLPa1NBEMe/s7tNXoxW1KJQKaUHkXhQvHgW6UHQQ09CBS/6V3hKc/AP8CqCrUcpmop3Cx48eDB4yEECjVQrlZb80CRN8t6OM/teagVxYZi38+Yz853dJbzoMV3MM8cJUcLMSUKIE8AzQ2PieZzFxEJOHMOgMQQ+dUgSAckNXhapU/NMhDSWLs1B24A8sO1xrN4NECkcAC9ASkiIJc6k5TRiUDPhnyMMdhKc+Zx19l6SgyeW76BEONY9exVQMzKExGKwwPsCzza7KGSSWRWEQhyEaDXp6ZHEr416ygbiKYOd7TEWvvcQIeusHYMJGhTwF9y7sGnSwaWyFAiyoxzqW0PM/RjghPxF2pWReAowTEXnDh0xgcLs8l2YQmOrj3N7ByiqEoH0cARs4u78WgAVkoEDIDoOi3AkcLOHU60RIg5wC4ZuTC7FaHKQm8Hq1fQuSOBvX/sodmNJSB5geaF5CPIkUeecdMxieoRO5jz9bheL6/tXjrwCyX/UYBUcjCaWHljx1xiX6z9xEjkYAzbGVnB8pvLmyXm9ep+W8CmsSHQQY77Zx1zboxAV0w7ybMhQmfqdmmw3nEp1I0Z+FGO6M8LZdoyZnuzzBdjISicKRnpxzI9fPb+0oYXsNdyi+d3h9bm9MWYHFtPeIZfLwzmFDKy1ai3p+PDls1Llz4yyFpferxjnyjJDSEy9CaCx5m2cJPerq6Xm34eTrZt3PqxYO1XOwDYZrFlH1fWnpU38Y9HRze3lj0vOujZcXKuuXm3jP+s3KbZVra7y2EAAAAAASUVORK5CYII=" alt="Poetry">
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://github.com/astral-sh/ruff">
|
|
19
|
+
<img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="ruff">
|
|
20
|
+
</a>
|
|
21
|
+
<a href="https://github.com/pre-commit/pre-commit">
|
|
22
|
+
<img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat-square" alt="pre-commit">
|
|
23
|
+
</a>
|
|
24
|
+
</p>
|
|
25
|
+
<p align="center">
|
|
26
|
+
<a href="https://pypi.org/project/ihexer/">
|
|
27
|
+
<img src="https://img.shields.io/pypi/v/ihexer.svg?logo=python&logoColor=fff&style=flat-square" alt="PyPI Version">
|
|
28
|
+
</a>
|
|
29
|
+
<img src="https://img.shields.io/pypi/pyversions/ihexer.svg?style=flat-square&logo=python&logoColor=fff" alt="Supported Python versions">
|
|
30
|
+
<img src="https://img.shields.io/pypi/l/ihexer.svg?style=flat-square" alt="License">
|
|
31
|
+
</p>
|
|
32
|
+
|
|
33
|
+
Parse, view and diff files in Intel HEX format.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
Install this via pip (or your favourite package manager):
|
|
38
|
+
|
|
39
|
+
`pip install ihexer`
|
|
40
|
+
|
|
41
|
+
## Start developing
|
|
42
|
+
|
|
43
|
+
The project uses Poetry for dependencies management and packaging.
|
|
44
|
+
Run the `bootstrap.ps1` script to install Python and create the virtual environment. (This is only working on windows machines!)
|
|
45
|
+
|
|
46
|
+
```powershell
|
|
47
|
+
.\bootstrap.ps1
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This will also generate a `poetry.lock` file, you should track this file in version control.
|
|
51
|
+
|
|
52
|
+
If you want to customize the bootstrapping process please create a bootstrap.json file according to the [bootstrap documentation](https://github.com/avengineers/bootstrap?tab=readme-ov-file#configuration).
|
|
53
|
+
|
|
54
|
+
To execute the test suite, call pytest inside Poetry's virtual environment via `poetry run`:
|
|
55
|
+
|
|
56
|
+
```shell
|
|
57
|
+
.venv/Scripts/poetry run pytest
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Check out the Poetry documentation for more information on the available commands.
|
|
61
|
+
|
|
62
|
+
For those using [VS Code](https://code.visualstudio.com/) there are tasks defined for the most common commands:
|
|
63
|
+
|
|
64
|
+
- bootstrap
|
|
65
|
+
- install dependencies
|
|
66
|
+
- run tests
|
|
67
|
+
- run all checks configured for pre-commit
|
|
68
|
+
- generate documentation
|
|
69
|
+
|
|
70
|
+
See the `.vscode/tasks.json` for more details.
|
|
71
|
+
|
|
72
|
+
## Committing changes
|
|
73
|
+
|
|
74
|
+
This repository uses [commitlint](https://github.com/conventional-changelog/commitlint) for checking if the commit message meets the [conventional commit format](https://www.conventionalcommits.org/en).
|
|
75
|
+
|
|
76
|
+
## Release
|
|
77
|
+
|
|
78
|
+
This repository uses [semantic release](https://python-semantic-release.readthedocs.io/en/latest/) to automate versioning the Python projects.
|
|
79
|
+
The package version will be automatically updated when the `develop` branch is built.
|
|
80
|
+
|
|
81
|
+
## Contributors ✨
|
|
82
|
+
|
|
83
|
+
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
84
|
+
|
|
85
|
+
<!-- prettier-ignore-start -->
|
|
86
|
+
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
|
87
|
+
<!-- markdownlint-disable -->
|
|
88
|
+
<!-- markdownlint-enable -->
|
|
89
|
+
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
90
|
+
<!-- prettier-ignore-end -->
|
|
91
|
+
|
|
92
|
+
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
|
93
|
+
|
|
94
|
+
## Credits
|
|
95
|
+
|
|
96
|
+
This package was created with
|
|
97
|
+
[Copier](https://copier.readthedocs.io/) and the
|
|
98
|
+
[cuinixam/pypackage-template](https://github.com/cuinixam/pypackage-template)
|
|
99
|
+
project template.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "ihexer"
|
|
3
|
+
version = "0.0.0"
|
|
4
|
+
description = "Parse, view and diff files in Intel HEX format."
|
|
5
|
+
authors = ["Avengineers <karsten.guenther@kamg.de>"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
repository = "https://github.com/avengineers/ihexer"
|
|
9
|
+
documentation = "https://ihexer.readthedocs.io"
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"Natural Language :: English",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
"Topic :: Software Development :: Libraries",
|
|
16
|
+
]
|
|
17
|
+
packages = [
|
|
18
|
+
{ include = "ihexer", from = "src" },
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[tool.poetry.urls]
|
|
22
|
+
"Bug Tracker" = "https://github.com/avengineers/ihexer/issues"
|
|
23
|
+
"Changelog" = "https://github.com/avengineers/ihexer/blob/develop/CHANGELOG.md"
|
|
24
|
+
|
|
25
|
+
[tool.poetry.dependencies]
|
|
26
|
+
python = ">=3.10,<3.14"
|
|
27
|
+
py-app-dev = "^2.1.0"
|
|
28
|
+
typer = "^0.12.3"
|
|
29
|
+
intelhex = "^2.3"
|
|
30
|
+
|
|
31
|
+
[tool.poetry.group.dev.dependencies]
|
|
32
|
+
pytest = "^7.0"
|
|
33
|
+
pytest-cov = "^4.0"
|
|
34
|
+
pre-commit = "^3.1.1"
|
|
35
|
+
ruff = "^0.3.0"
|
|
36
|
+
pypeline-runner = "^0.3"
|
|
37
|
+
python-semantic-release = "^9.1"
|
|
38
|
+
|
|
39
|
+
[tool.poetry.group.docs.dependencies]
|
|
40
|
+
myst-parser = ">=0.16"
|
|
41
|
+
sphinx = "^7.3.5"
|
|
42
|
+
sphinxcontrib-mermaid = "^0.9.2"
|
|
43
|
+
mlx-traceability = "^10.0.0"
|
|
44
|
+
sphinx-copybutton = "^0.5.2"
|
|
45
|
+
sphinx-new-tab-link = "^0.4.0"
|
|
46
|
+
sphinx-book-theme = "^1.1.2"
|
|
47
|
+
sphinx-design = "^0.5.0"
|
|
48
|
+
|
|
49
|
+
[tool.semantic_release]
|
|
50
|
+
version_toml = ["pyproject.toml:tool.poetry.version"]
|
|
51
|
+
version_variables = [
|
|
52
|
+
"src/ihexer/__init__.py:__version__",
|
|
53
|
+
"docs/conf.py:release",
|
|
54
|
+
]
|
|
55
|
+
build_command = "pip install poetry && poetry build"
|
|
56
|
+
|
|
57
|
+
[tool.semantic_release.changelog]
|
|
58
|
+
exclude_commit_patterns = [
|
|
59
|
+
"chore*",
|
|
60
|
+
"ci*",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.semantic_release.changelog.environment]
|
|
64
|
+
keep_trailing_newline = true
|
|
65
|
+
|
|
66
|
+
[tool.semantic_release.branches.main]
|
|
67
|
+
match = "develop"
|
|
68
|
+
|
|
69
|
+
[tool.semantic_release.branches.noop]
|
|
70
|
+
match = "(?!develop$)"
|
|
71
|
+
prerelease = true
|
|
72
|
+
|
|
73
|
+
[tool.pytest.ini_options]
|
|
74
|
+
addopts = "-v -Wdefault --cov=ihexer --cov-report=term-missing:skip-covered"
|
|
75
|
+
pythonpath = ["src"]
|
|
76
|
+
|
|
77
|
+
[tool.coverage.run]
|
|
78
|
+
branch = true
|
|
79
|
+
|
|
80
|
+
[tool.coverage.report]
|
|
81
|
+
exclude_lines = [
|
|
82
|
+
"pragma: no cover",
|
|
83
|
+
"@overload",
|
|
84
|
+
"if TYPE_CHECKING",
|
|
85
|
+
"raise NotImplementedError",
|
|
86
|
+
'if __name__ == "__main__":',
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[tool.ruff]
|
|
90
|
+
target-version = "py38"
|
|
91
|
+
line-length = 180
|
|
92
|
+
lint.ignore = [
|
|
93
|
+
"D203", # 1 blank line required before class docstring
|
|
94
|
+
"D212", # Multi-line docstring summary should start at the first line
|
|
95
|
+
"D100", # Missing docstring in public module
|
|
96
|
+
"D101", # Missing docstring in public class
|
|
97
|
+
"D102", # Missing docstring in public method
|
|
98
|
+
"D103", # Missing docstring in public function
|
|
99
|
+
"D104", # Missing docstring in public package
|
|
100
|
+
"D107", # Missing docstring in `__init__`
|
|
101
|
+
"D401", # First line of docstring should be in imperative mood
|
|
102
|
+
]
|
|
103
|
+
lint.select = [
|
|
104
|
+
"B", # flake8-bugbear
|
|
105
|
+
"D", # flake8-docstrings
|
|
106
|
+
"C4", # flake8-comprehensions
|
|
107
|
+
"S", # flake8-bandit
|
|
108
|
+
"F", # pyflake
|
|
109
|
+
"E", # pycodestyle
|
|
110
|
+
"W", # pycodestyle
|
|
111
|
+
"UP", # pyupgrade
|
|
112
|
+
"I", # isort
|
|
113
|
+
"RUF", # ruff specific
|
|
114
|
+
]
|
|
115
|
+
exclude = [
|
|
116
|
+
"bootstrap.py"
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
[tool.ruff.lint.per-file-ignores]
|
|
120
|
+
"tests/**/*" = [
|
|
121
|
+
"D100",
|
|
122
|
+
"D101",
|
|
123
|
+
"D102",
|
|
124
|
+
"D103",
|
|
125
|
+
"D104",
|
|
126
|
+
"S101",
|
|
127
|
+
]
|
|
128
|
+
"setup.py" = ["D100"]
|
|
129
|
+
"conftest.py" = ["D100"]
|
|
130
|
+
"docs/conf.py" = ["D100"]
|
|
131
|
+
|
|
132
|
+
[tool.ruff.lint.isort]
|
|
133
|
+
known-first-party = ["ihexer", "tests"]
|
|
134
|
+
|
|
135
|
+
[tool.mypy]
|
|
136
|
+
check_untyped_defs = true
|
|
137
|
+
disallow_any_generics = true
|
|
138
|
+
disallow_incomplete_defs = true
|
|
139
|
+
disallow_untyped_defs = true
|
|
140
|
+
mypy_path = "src/"
|
|
141
|
+
no_implicit_optional = true
|
|
142
|
+
show_error_codes = true
|
|
143
|
+
warn_unreachable = true
|
|
144
|
+
warn_unused_ignores = true
|
|
145
|
+
exclude = [
|
|
146
|
+
'docs/.*',
|
|
147
|
+
'setup.py',
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
[[tool.mypy.overrides]]
|
|
151
|
+
module = "tests.*"
|
|
152
|
+
allow_untyped_defs = true
|
|
153
|
+
|
|
154
|
+
[[tool.mypy.overrides]]
|
|
155
|
+
module = "docs.*"
|
|
156
|
+
ignore_errors = true
|
|
157
|
+
|
|
158
|
+
[build-system]
|
|
159
|
+
requires = ["poetry-core>=1.0.0"]
|
|
160
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.0"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Used to run ihexer from the command line when run from this repository.
|
|
3
|
+
|
|
4
|
+
This is required because ihexer module is not visible when running from the repository.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import runpy
|
|
8
|
+
import sys
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
sys.path.insert(0, Path(__file__).parent.parent.absolute().as_posix())
|
|
12
|
+
runpy.run_module("ihexer.main", run_name="__main__")
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import difflib
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from intelhex import IntelHex as _IntelHex
|
|
7
|
+
from py_app_dev.core.exceptions import UserNotificationException
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class IntelHexSegment:
|
|
12
|
+
start: int
|
|
13
|
+
end: int
|
|
14
|
+
data: bytes
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def length(self) -> int:
|
|
18
|
+
return self.end - self.start + 1
|
|
19
|
+
|
|
20
|
+
def __post_init__(self) -> None:
|
|
21
|
+
"""Make sure the segment can have a size."""
|
|
22
|
+
if self.end < self.start:
|
|
23
|
+
raise ValueError(f"Segments length has negative size: end {self.end} < start {self.start}.")
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
"""Only print the address range."""
|
|
27
|
+
return f"{hex(self.start)}-{hex(self.end)}: {self.length} bytes"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class IntelHex:
|
|
32
|
+
start_address: int
|
|
33
|
+
end_address: int
|
|
34
|
+
segments: List[IntelHexSegment]
|
|
35
|
+
|
|
36
|
+
def get_content(self) -> str:
|
|
37
|
+
"""Return the hex content of all segments as a single long string."""
|
|
38
|
+
hex_content = []
|
|
39
|
+
for segment in self.segments:
|
|
40
|
+
hex_content.append(segment.data.hex().upper())
|
|
41
|
+
return "".join(hex_content)
|
|
42
|
+
|
|
43
|
+
def __str__(self) -> str:
|
|
44
|
+
"""Return the content when the object is printed."""
|
|
45
|
+
return self.get_content()
|
|
46
|
+
|
|
47
|
+
def write_hex_file(self, output_file: Path) -> None:
|
|
48
|
+
"""Write the IntelHex content back to a hex file."""
|
|
49
|
+
# Create a new _IntelHex object to store the data
|
|
50
|
+
intel_hex = _IntelHex()
|
|
51
|
+
|
|
52
|
+
# For each segment, add its data to the _IntelHex object
|
|
53
|
+
for segment in self.segments:
|
|
54
|
+
intel_hex.puts(segment.start, segment.data)
|
|
55
|
+
|
|
56
|
+
# Write the data to the specified file
|
|
57
|
+
intel_hex.write_hex_file(output_file.as_posix())
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class IntelHexParser:
|
|
61
|
+
def __init__(self, file: Path, bytes_swap: bool = False, word_size: int = 4) -> None:
|
|
62
|
+
self.file = file
|
|
63
|
+
self.bytes_swap = bytes_swap
|
|
64
|
+
self.word_size = word_size
|
|
65
|
+
|
|
66
|
+
def parse(self) -> IntelHex:
|
|
67
|
+
segments = self._read_segments(self._parse_content())
|
|
68
|
+
return IntelHex(
|
|
69
|
+
min([segment.start for segment in segments]),
|
|
70
|
+
max([segment.end for segment in segments]),
|
|
71
|
+
segments,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def _parse_content(self) -> _IntelHex:
|
|
75
|
+
_content = _IntelHex()
|
|
76
|
+
format = self.file.suffix.lower().replace(".", "")
|
|
77
|
+
try:
|
|
78
|
+
_content.loadfile(self.file.as_posix(), format)
|
|
79
|
+
except ValueError as e:
|
|
80
|
+
raise UserNotificationException(f"Could not load '{self.file}'. Unsupported format {format}") from e
|
|
81
|
+
return _content
|
|
82
|
+
|
|
83
|
+
def _read_segments(self, content: _IntelHex) -> List[IntelHexSegment]:
|
|
84
|
+
segments: List[IntelHexSegment] = []
|
|
85
|
+
_segments = content.segments()
|
|
86
|
+
if _segments:
|
|
87
|
+
for segment_addresses_list in _segments:
|
|
88
|
+
start_address = segment_addresses_list[0]
|
|
89
|
+
# For some reason IntelHex sets the end address to the next available address. Bug?
|
|
90
|
+
end_address = segment_addresses_list[1] - 1
|
|
91
|
+
data: bytes = content.gets(start_address, end_address - start_address + 1)
|
|
92
|
+
if self.bytes_swap:
|
|
93
|
+
data = self.swap_bytes(data, self.word_size)
|
|
94
|
+
segments.append(
|
|
95
|
+
IntelHexSegment(
|
|
96
|
+
start_address,
|
|
97
|
+
end_address,
|
|
98
|
+
data,
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
return segments
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def swap_bytes(data: bytes, word_size: int) -> bytes:
|
|
105
|
+
if len(data) % word_size != 0:
|
|
106
|
+
raise UserNotificationException(f"Data length {len(data)} is not a multiple of word size {word_size}.")
|
|
107
|
+
swapped_data = bytearray()
|
|
108
|
+
for i in range(0, len(data), word_size):
|
|
109
|
+
swapped_data.extend(reversed(data[i : i + word_size]))
|
|
110
|
+
return bytes(swapped_data)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class IntelHexPrinter:
|
|
114
|
+
def __init__(self, content: IntelHex) -> None:
|
|
115
|
+
self.content = content
|
|
116
|
+
|
|
117
|
+
@staticmethod
|
|
118
|
+
def stringify_segment(segment: IntelHexSegment) -> List[str]:
|
|
119
|
+
output_lines = []
|
|
120
|
+
# Calculate the alignment offset from the nearest lower multiple of 16
|
|
121
|
+
alignment_offset = segment.start % 16
|
|
122
|
+
|
|
123
|
+
# Adjust the start address to be aligned to 16 bytes
|
|
124
|
+
aligned_start_address = segment.start - alignment_offset
|
|
125
|
+
|
|
126
|
+
# Calculate the number of lines, ensuring at least one line
|
|
127
|
+
# Include the offset in the calculation for total data length
|
|
128
|
+
num_lines = (len(segment.data) + alignment_offset + 15) // 16 if segment.data else 1
|
|
129
|
+
|
|
130
|
+
for i in range(num_lines):
|
|
131
|
+
start_idx = i * 16 - alignment_offset
|
|
132
|
+
end_idx = start_idx + 16
|
|
133
|
+
current_data_chunk = segment.data[max(0, start_idx) : end_idx]
|
|
134
|
+
|
|
135
|
+
address_str = f"{aligned_start_address + i*16:08X}"
|
|
136
|
+
|
|
137
|
+
# Prepare the data bytes, filling missing bytes with '--' at the beginning due to alignment
|
|
138
|
+
data_strs = ["--"] * max(0, -start_idx) # Fill with '--' if start_idx is negative
|
|
139
|
+
data_strs += [f"{byte:02X}" for byte in current_data_chunk]
|
|
140
|
+
missing_bytes = 16 - len(data_strs)
|
|
141
|
+
data_strs.extend(["--"] * missing_bytes)
|
|
142
|
+
|
|
143
|
+
data_str = " ".join(data_strs)
|
|
144
|
+
segment_str = f"{address_str}: {data_str}"
|
|
145
|
+
output_lines.append(segment_str)
|
|
146
|
+
return output_lines
|
|
147
|
+
|
|
148
|
+
@staticmethod
|
|
149
|
+
def stringify_segments(segments: List[IntelHexSegment]) -> List[str]:
|
|
150
|
+
output_lines = []
|
|
151
|
+
for segment in segments:
|
|
152
|
+
output_lines.extend(IntelHexPrinter.stringify_segment(segment))
|
|
153
|
+
return output_lines
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def stringify_short_info(segments: List[IntelHexSegment]) -> List[str]:
|
|
157
|
+
delimiter = "-" * 57
|
|
158
|
+
output_lines = [delimiter, "SEGMENTS:"]
|
|
159
|
+
for segment in segments:
|
|
160
|
+
output_lines.append(str(segment))
|
|
161
|
+
output_lines.append(delimiter)
|
|
162
|
+
return output_lines
|
|
163
|
+
|
|
164
|
+
def to_string(self, with_short_info: bool = True) -> str:
|
|
165
|
+
content = []
|
|
166
|
+
if with_short_info:
|
|
167
|
+
content.extend(IntelHexPrinter.stringify_short_info(self.content.segments))
|
|
168
|
+
content.extend(IntelHexPrinter.stringify_segments(self.content.segments))
|
|
169
|
+
return "\n".join(content)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class IntelHexDiff:
|
|
173
|
+
def __init__(self, first: Path, second: Path) -> None:
|
|
174
|
+
self.first_file = first
|
|
175
|
+
self.second_file = second
|
|
176
|
+
|
|
177
|
+
def generate_html(self) -> str:
|
|
178
|
+
first_list = IntelHexPrinter.stringify_segments(IntelHexParser(self.first_file).parse().segments)
|
|
179
|
+
second_list = IntelHexPrinter.stringify_segments(IntelHexParser(self.second_file).parse().segments)
|
|
180
|
+
|
|
181
|
+
html_diff = difflib.HtmlDiff()
|
|
182
|
+
|
|
183
|
+
return html_diff.make_file(
|
|
184
|
+
first_list,
|
|
185
|
+
second_list,
|
|
186
|
+
fromdesc=f"{self.first_file}",
|
|
187
|
+
todesc=f"{self.second_file}",
|
|
188
|
+
context=True,
|
|
189
|
+
numlines=5,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def generate(self, out_file: Path) -> None:
|
|
193
|
+
out_file.write_text(self.generate_html())
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
from py_app_dev.core.exceptions import UserNotificationException
|
|
7
|
+
from py_app_dev.core.logging import logger, setup_logger, time_it
|
|
8
|
+
|
|
9
|
+
from ihexer import __version__
|
|
10
|
+
from ihexer.ihexer import IntelHexDiff, IntelHexParser, IntelHexPrinter
|
|
11
|
+
|
|
12
|
+
package_name = "ihexer"
|
|
13
|
+
|
|
14
|
+
app = typer.Typer(name=package_name, help="Intel HEX utils.", no_args_is_help=True)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.callback(invoke_without_command=True)
|
|
18
|
+
def version(
|
|
19
|
+
version: bool = typer.Option(None, "--version", "-v", is_eager=True, help="Show version and exit."),
|
|
20
|
+
) -> None:
|
|
21
|
+
if version:
|
|
22
|
+
typer.echo(f"{package_name} {__version__}")
|
|
23
|
+
raise typer.Exit()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@app.command()
|
|
27
|
+
@time_it("view")
|
|
28
|
+
def view(
|
|
29
|
+
file: Path = typer.Option(help="Input hex/bin file."), # noqa: B008
|
|
30
|
+
dump_to: Optional[Path] = typer.Option(None, help="Output file to dump the hex content."), # noqa: B008
|
|
31
|
+
short_info: bool = typer.Option(True, help="Dump hex file short info before the content."),
|
|
32
|
+
) -> None:
|
|
33
|
+
intel_hex = IntelHexParser(file).parse()
|
|
34
|
+
print(IntelHexPrinter(intel_hex).to_string(short_info))
|
|
35
|
+
if dump_to:
|
|
36
|
+
dump_to.write_text(IntelHexPrinter(intel_hex).to_string(short_info))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@app.command()
|
|
40
|
+
@time_it("diff")
|
|
41
|
+
def diff(
|
|
42
|
+
first: Path = typer.Option(help="First input hex/bin file."), # noqa: B008
|
|
43
|
+
second: Path = typer.Option(help="Second input hex/bin file."), # noqa: B008
|
|
44
|
+
out_file: Path = typer.Option(help="Output file to write the html diff report."), # noqa: B008
|
|
45
|
+
) -> None:
|
|
46
|
+
IntelHexDiff(first, second).generate(out_file)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@app.command()
|
|
50
|
+
@time_it("info")
|
|
51
|
+
def info(
|
|
52
|
+
file: Path = typer.Option(help="Input hex/bin file."), # noqa: B008
|
|
53
|
+
) -> None:
|
|
54
|
+
intel_hex = IntelHexParser(file).parse()
|
|
55
|
+
for line in IntelHexPrinter.stringify_short_info(intel_hex.segments):
|
|
56
|
+
print(line)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@app.command()
|
|
60
|
+
@time_it("swap")
|
|
61
|
+
def swap(
|
|
62
|
+
file: Path = typer.Option(help="Input hex/bin file."), # noqa: B008
|
|
63
|
+
out_file: Optional[Path] = typer.Option(None, help="Output file to write the swapped hex content."), # noqa: B008
|
|
64
|
+
word_size: int = typer.Option(4, help="Word size in bytes."),
|
|
65
|
+
) -> None:
|
|
66
|
+
IntelHexParser(file, bytes_swap=True, word_size=word_size).parse().write_hex_file(out_file or file)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def main() -> int:
|
|
70
|
+
try:
|
|
71
|
+
setup_logger()
|
|
72
|
+
app()
|
|
73
|
+
return 0
|
|
74
|
+
except UserNotificationException as e:
|
|
75
|
+
logger.error(f"{e}")
|
|
76
|
+
return 1
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
if __name__ == "__main__":
|
|
80
|
+
sys.exit(main())
|
|
File without changes
|