First Commit
On branch master Initial commit Changes to be committed: new file: .gitignore new file: .python-version new file: README.md new file: main.py new file: pyproject.toml new file: src/constructor/__init__.py new file: src/constructor/construct.py
This commit is contained in:
177
.gitignore
vendored
Normal file
177
.gitignore
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
# ---> Python
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.12
|
||||
6
main.py
Normal file
6
main.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def main():
|
||||
print("Hello from constructor!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
13
pyproject.toml
Normal file
13
pyproject.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
[project]
|
||||
name = "constructor"
|
||||
version = "0.1.0"
|
||||
description = "Custom Wrapper Around builtin Struct class"
|
||||
authors = [{name = "TSD", email = "t.s.davis6291@gmail.com"}]
|
||||
readme = "README.md"
|
||||
license = "APACHE2.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = []
|
||||
|
||||
[build-system]
|
||||
requires = ["uv-build"]
|
||||
build-backend = "uv_build"
|
||||
11
src/constructor/__init__.py
Normal file
11
src/constructor/__init__.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from construct import (
|
||||
struct_formatter,
|
||||
structBSA,
|
||||
bType
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'struct_formatter',
|
||||
'structBSA',
|
||||
'bType'
|
||||
]
|
||||
377
src/constructor/construct.py
Normal file
377
src/constructor/construct.py
Normal file
@@ -0,0 +1,377 @@
|
||||
import struct
|
||||
from dataclasses import dataclass,asdict
|
||||
import sys
|
||||
import platform
|
||||
|
||||
class structBSA:
|
||||
|
||||
_validators = '@=<>!'
|
||||
_values = {
|
||||
'@' : ( 'native' , 'native' , 'native' ),
|
||||
'=' : ( 'native' , 'standard' , None ),
|
||||
'<' : ( 'little-endian' , 'standard' , None ),
|
||||
'>' : ( 'big-endian' , 'standard' , None ),
|
||||
'!' : ( 'network' , 'standard' , None )
|
||||
}
|
||||
|
||||
_native = sys.byteorder
|
||||
_arch = platform.machine()
|
||||
|
||||
if '64' in _arch:
|
||||
_size = 64
|
||||
|
||||
elif '32' in _arch or '86' in _arch:
|
||||
_size = 32
|
||||
|
||||
else:
|
||||
_size = None
|
||||
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
char:str
|
||||
) -> None:
|
||||
|
||||
if char in self._validators:
|
||||
|
||||
self.char = char
|
||||
|
||||
self.byte_order,\
|
||||
self.size,\
|
||||
self.alignment = self._values[char]
|
||||
|
||||
def __repr__(self):
|
||||
result = ''
|
||||
values = {
|
||||
'Native' : self._native,
|
||||
'Architecture' : self._arch,
|
||||
'AddressSpace' : self._size,
|
||||
'Character' : self.char,
|
||||
'ByteOrder' : self.byte_order,
|
||||
'Size' : self.size,
|
||||
'Alignment' : self.alignment
|
||||
}
|
||||
|
||||
|
||||
ct_1,\
|
||||
ct_2,\
|
||||
cb_1,\
|
||||
cb_2 = ('\u250E','\u2512','\u2516','\u251A')
|
||||
|
||||
jl,\
|
||||
jr,\
|
||||
jd,\
|
||||
ju,\
|
||||
jq = ('\u2520','\u2528','\u252F','\u2537','\u253f')
|
||||
|
||||
edges = ('\u2502','\u2501')
|
||||
ev,eh = edges
|
||||
|
||||
lines:list[
|
||||
list[str]
|
||||
] = [[],[],[],[],[]]
|
||||
|
||||
n = 0
|
||||
n_stop = len(values) - 1
|
||||
|
||||
for header,value in values.items():
|
||||
|
||||
line_1 = ''
|
||||
line_2 = ''
|
||||
line_3 = ''
|
||||
line_4 = ''
|
||||
line_5 = ''
|
||||
value = str(value)
|
||||
|
||||
field_length = max(len(header),len(value)) + 2
|
||||
|
||||
if len(header) %2 != 0:
|
||||
header += ' '
|
||||
|
||||
if len(value) %2 != 0:
|
||||
value += ' '
|
||||
|
||||
while len(header) < field_length:
|
||||
|
||||
header = f' {header} '
|
||||
|
||||
if len(header) >= field_length:
|
||||
break
|
||||
|
||||
while len(value) < field_length:
|
||||
|
||||
value = f' {value} '
|
||||
|
||||
if len(value) >= field_length:
|
||||
break
|
||||
|
||||
|
||||
line_2 += f'{ev}{header}'
|
||||
line_4 += f'{ev}{value}'
|
||||
|
||||
match n:
|
||||
case 0:
|
||||
|
||||
line_1 = f'{ct_1}{eh * field_length}'
|
||||
|
||||
line_3 = f'{jl}{eh * field_length}'
|
||||
|
||||
line_5 = f'{cb_1}{eh*field_length}'
|
||||
|
||||
case _:
|
||||
|
||||
line_1 = f'{jd}{eh*field_length}'
|
||||
|
||||
line_3 = f'{jq}{eh*field_length}'
|
||||
|
||||
line_5 = f'{ju}{eh*field_length}'
|
||||
|
||||
if len(line_2) != len(line_3):
|
||||
|
||||
while len(line_2) != len(line_3):
|
||||
|
||||
line_1 += eh
|
||||
line_3 += eh
|
||||
line_5 += eh
|
||||
|
||||
if len(line_2) == len(line_3):
|
||||
break
|
||||
|
||||
if n == n_stop:
|
||||
|
||||
line_1 += ct_2
|
||||
line_2 += ev
|
||||
line_3 += jr
|
||||
line_4 += ev
|
||||
line_5 += cb_2
|
||||
|
||||
|
||||
|
||||
lines[0].append(line_1)
|
||||
lines[1].append(line_2)
|
||||
lines[2].append(line_3)
|
||||
lines[3].append(line_4)
|
||||
lines[4].append(line_5)
|
||||
n += 1
|
||||
|
||||
for line in lines:
|
||||
full_line = ''.join(line)
|
||||
result += f'{full_line}\n'
|
||||
|
||||
return result
|
||||
|
||||
def __str__(self):
|
||||
|
||||
return self.char
|
||||
|
||||
|
||||
|
||||
class bType(str):
|
||||
|
||||
def __new__(cls,designator):
|
||||
return super().__new__(cls, designator)
|
||||
|
||||
_values = {
|
||||
'x' : ( '\x00' , None , 0 ),
|
||||
'c' : ( 'char' , bytes , 1 ),
|
||||
'b' : ( 'signed char' , int , 1 ),
|
||||
'B' : ( 'unsigned char' , int , 1 ),
|
||||
'?' : ( '_Bool' , bool , 1 ),
|
||||
'h' : ( 'short' , int , 2 ),
|
||||
'H' : ( 'unsigned short' , int , 2 ),
|
||||
'i' : ( 'integer' , int , 4 ),
|
||||
'I' : ( 'unsigned integer' , int , 4 ),
|
||||
'l' : ( 'long' , int , 4 ),
|
||||
'L' : ( 'unsigned long' , int , 4 ),
|
||||
'q' : ( 'long long' , int , 8 ),
|
||||
'Q' : ( 'unsigned long long' , int , 8 ),
|
||||
'n' : ( 'ssize_t' , int , None ),
|
||||
'N' : ( 'size_t' , int , None ),
|
||||
'e' : ( 'f16' , float , 2 ),
|
||||
'f' : ( 'f32' , float , 4 ),
|
||||
'd' : ( 'f64' , float , 8 ),
|
||||
's' : ( 'char[]' , bytes , None ),
|
||||
'p' : ( 'char[]' , bytes , None ),
|
||||
'P' : ( 'void*' , int , None )
|
||||
}
|
||||
|
||||
def __init__(self,designator:str):
|
||||
self.designator = designator
|
||||
self.c_type,self.type_of,self.size = self._values[designator]
|
||||
|
||||
def __repr__(self):
|
||||
|
||||
headers = (
|
||||
'Symbol',
|
||||
'CType' ,
|
||||
'PyType',
|
||||
'Size'
|
||||
)
|
||||
|
||||
title = 'DataStructure'
|
||||
|
||||
type_1 = f'{self.c_type}'
|
||||
type_2 = f'{str(self.type_of).replace('class ','')}'
|
||||
|
||||
size = f'{self.size}' if self.size is not None else '0x00'
|
||||
|
||||
align_val = max([len(s) for s in [type_1,type_2,size,*headers]]) + 2
|
||||
|
||||
top_line = ' ' + ('\u2500' * (((align_val + 1) * 4) - 1))
|
||||
line_1 = ' \u2502'
|
||||
|
||||
for header in headers:
|
||||
|
||||
unused_space = (align_val - len(header)) // 2
|
||||
|
||||
if len(header) + (unused_space * 2) != align_val:
|
||||
header = ' ' + header
|
||||
line_1 += f'{' ' * unused_space}{header}{' ' * unused_space}\u2502'
|
||||
|
||||
line_2 = ' \u251c'
|
||||
|
||||
for _ in range(4):
|
||||
line_2 += ('\u2500' * (align_val)) + '\u253C'
|
||||
|
||||
line_2 = f'{line_2[:-1]}\u2524'
|
||||
line_3 = ' \u2502'
|
||||
|
||||
if self.designator == 'x':
|
||||
type_1 = 'NULL'
|
||||
|
||||
for data in (
|
||||
self.designator,
|
||||
type_1,
|
||||
type_2,
|
||||
size
|
||||
):
|
||||
|
||||
unused_space = (align_val - len(data)) // 2
|
||||
|
||||
if len(data) + (unused_space*2) != align_val:
|
||||
data = ' ' + data
|
||||
|
||||
line_3 += f'{' ' * unused_space}{data}{' ' * unused_space}\u2502'
|
||||
|
||||
line_4 = top_line
|
||||
title_spacer = (len(top_line) // 2) - (len(title) // 2)
|
||||
title = f'{' '*title_spacer}{title}'
|
||||
|
||||
top_line = f' \u250C{top_line[2:]}\u2510'
|
||||
line_4 = f' \u2514{line_4[2:]}\u2518'
|
||||
|
||||
return '\n'.join([title,top_line,line_1,line_2,line_3,line_4])
|
||||
|
||||
def __str__(self):
|
||||
return self.designator
|
||||
|
||||
@property
|
||||
def c(self):
|
||||
return self.designator
|
||||
|
||||
|
||||
@dataclass
|
||||
class struct_formatter:
|
||||
|
||||
native :structBSA = structBSA('@')
|
||||
native_std :structBSA = structBSA('=')
|
||||
little_end :structBSA = structBSA('<')
|
||||
big_end :structBSA = structBSA('>')
|
||||
network :structBSA = structBSA('!')
|
||||
|
||||
pad :bType = bType('x')
|
||||
char :bType = bType('c')
|
||||
schar :bType = bType('b')
|
||||
uchar :bType = bType('B')
|
||||
_Bool :bType = bType('?')
|
||||
short :bType = bType('h')
|
||||
ushort :bType = bType('H')
|
||||
integer :bType = bType('i')
|
||||
uint :bType = bType('I')
|
||||
long :bType = bType('l')
|
||||
ulong :bType = bType('L')
|
||||
longlong :bType = bType('q')
|
||||
ulonglong :bType = bType('Q')
|
||||
ssize_t :bType = bType('n')
|
||||
size_t :bType = bType('N')
|
||||
f16 :bType = bType('e')
|
||||
f32 :bType = bType('f')
|
||||
f64 :bType = bType('d')
|
||||
string :bType = bType('s')
|
||||
pascal :bType = bType('p')
|
||||
void :bType = bType('P')
|
||||
|
||||
def _todict(self) -> dict[str,structBSA|bType]:
|
||||
return asdict(self)
|
||||
|
||||
def compile(self,*args):
|
||||
str_head = ''
|
||||
str_tail = ''
|
||||
for arg in args:
|
||||
match arg:
|
||||
case structBSA():
|
||||
str_head += arg.char
|
||||
case tuple():
|
||||
str_plus = ''.join([str(a) for a in arg])
|
||||
str_tail += str_plus
|
||||
case bType():
|
||||
str_tail += arg
|
||||
case str():
|
||||
if arg in structBSA._validators:
|
||||
str_head += arg
|
||||
else:
|
||||
str_tail += arg
|
||||
case int():
|
||||
str_tail += str(arg)
|
||||
case _:
|
||||
raise NotImplementedError
|
||||
return struct.Struct(f'{str_head}{str_tail}')
|
||||
|
||||
def __repr__(self):
|
||||
result = ''
|
||||
struct_formats:list[tuple[str,structBSA]] = []
|
||||
struct_types:list[tuple[str,bType]] = []
|
||||
formatter = struct_formatter()._todict()
|
||||
for name,inst in formatter.items():
|
||||
match inst:
|
||||
case structBSA():
|
||||
struct_formats.append((name,inst))
|
||||
case bType():
|
||||
struct_types.append((name,inst))
|
||||
splicer = '\u252D\u2502\u253d\u2502\u2535'
|
||||
for fmt in struct_formats:
|
||||
for s_type in struct_types:
|
||||
f_name,s_fmt = fmt
|
||||
s_name,s_typ = s_type
|
||||
fmt_lines = repr(s_fmt).splitlines()
|
||||
typ_lines = repr(s_typ).splitlines()
|
||||
lengths = []
|
||||
for idx,line in enumerate(fmt_lines):
|
||||
lengths.append(len(f'{typ_lines[idx]}{line}'))
|
||||
new_repr = []
|
||||
|
||||
max_length = 4 + max(lengths)
|
||||
title = typ_lines[0]
|
||||
for idx,f_line in enumerate(fmt_lines):
|
||||
t_line = splicer[idx] + typ_lines[idx+1][2:]
|
||||
f_line = f_line[:-1]
|
||||
spacer = max_length - len(f'\u2503 {f_line}{t_line}')
|
||||
new_repr.append(f'\u2503 {f_line}{t_line}{' '*spacer}\u2503')
|
||||
result += (f'\u250F{'\u2501' * (max_length - 1)}\u2513\n')
|
||||
result += (
|
||||
f'\u2503 {title.lstrip()}: {f_name}+{s_name} -> [\'{s_fmt.char}{s_typ.designator}\']{' '*(max_length - len(f'\u2503 {title.lstrip()}: {f_name}+{s_name} -> [\'{s_fmt.char}{s_typ.designator}\']'))}\u2503\n'
|
||||
)
|
||||
result += (f'\u2503{' '*((max_length - 1))}\u2503\n{'\n'.join(new_repr)}\n')
|
||||
result += (f'\u2503{' '*(max_length - 1)}\u2503\n\u2517{'\u2501' * (max_length - 1)}\u251b\n')
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
s_dict = struct_formatter()._todict()
|
||||
for key,value in s_dict.items():
|
||||
spacer = (max([len(v) for v in repr(value).splitlines()]) // 2) - (len(key) // 2)
|
||||
print(f'{' ' * spacer}{key}\n{value}')
|
||||
print('\n\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user