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:
2025-10-16 20:52:36 -05:00
commit d65becb17e
7 changed files with 585 additions and 0 deletions

177
.gitignore vendored Normal file
View 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
View File

@@ -0,0 +1 @@
3.12

0
README.md Normal file
View File

6
main.py Normal file
View File

@@ -0,0 +1,6 @@
def main():
print("Hello from constructor!")
if __name__ == "__main__":
main()

13
pyproject.toml Normal file
View 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"

View File

@@ -0,0 +1,11 @@
from construct import (
struct_formatter,
structBSA,
bType
)
__all__ = [
'struct_formatter',
'structBSA',
'bType'
]

View 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()