Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
17e17c9
fix: update exception info to hint at improper attr names in data for…
Jan 10, 2026
236adcb
fix setting dump indent level to 0 for nested structures - exception …
Jan 12, 2026
b84335d
update xmlbuildmanual docstrings to reflect correct understanding
Jan 12, 2026
e25a789
fix missing method names from rename protect list. add test for check
Jan 12, 2026
afd31a5
fix internal name re-assign ignore switch not working. update test fo…
Jan 12, 2026
5ae281c
fix: unicode decode error on creating file hash of binary files. repl…
Feb 5, 2026
b26d894
fix: name collisons in loaddict for maci internal names. change attr …
Feb 5, 2026
27a551c
nit: use var for search name
Feb 5, 2026
0c263b4
add: xmldump ability to set indentation and xml declaration, set as d…
Feb 7, 2026
da62017
perf: improve bool op on obj by 99% reduction. add explicit tests
Feb 7, 2026
eb0688e
ignore coverage for py38 on version check
Feb 7, 2026
9196250
fix,perf: obj not having true equality if attr order changes. improve…
Feb 7, 2026
27381f8
add: hashability on object for general use, and lru_cache requirement
Feb 10, 2026
bf69f60
perf: improve op time on methods via functool caching. requires updat…
Feb 10, 2026
4f7e9cf
add: len ability on obj. cleanup bool, update return types. add/updat…
Feb 21, 2026
31510be
perf: add user attr assignment tracker to improve operators, organiza…
Feb 21, 2026
f07de20
perf: add internal cacher improving perf on some methods averaging 86…
Feb 21, 2026
d6e53b9
update: type interfaces on stub. add tests
Feb 25, 2026
cb5c77d
fix: loaddict and loadstrdict returning extra internal data. update t…
Feb 25, 2026
c1f333d
update wheel pkg dep for safety compat for py38
Mar 7, 2026
42ea0b3
fix win test: failing auto decode on data
Mar 7, 2026
b516744
remove incorrect file hashing causing mismatch on windows os. remove …
Mar 8, 2026
ed7a8c6
update type testing
Mar 8, 2026
a23b710
update: deploy debug msgs
Mar 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions deploy/build_deploy_maci.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,27 +100,36 @@


# Build Wheel from Setup, then Publish to PyPI
subprocess.run(('python3', '-B', 'setup.py', MACI_VERSION, 'sdist', 'bdist_wheel')).check_returncode()
subprocess.run(('python3', '-B', '-m', 'twine', 'upload', '--repository', DEPLOY_TYPE, *glob('dist/*'), '--verbose')).check_returncode()
cmd_output = subprocess.run(('python3', '-B', 'setup.py', MACI_VERSION, 'sdist', 'bdist_wheel'), capture_output=True)
cmd_output.check_returncode()

cmd_output = subprocess.run(('python3', '-B', '-m', 'twine', 'upload', '--repository', DEPLOY_TYPE, *glob('dist/*'), '--verbose'), capture_output=True)
cmd_output.check_returncode()
print('SUCCESS: maci deployment')


### GITHUB ###

# Clone and Tag New Release Number if required
if DEPLOY_TYPE not in ignore_github_deploy_list:
subprocess.run(('git', 'clone', GITHUB_MACI_REPO, './maci_tag')).check_returncode()
cmd_output = subprocess.run(('git', 'clone', GITHUB_MACI_REPO, './maci_tag'), capture_output=True)
cmd_output.check_returncode()

os.chdir('maci_tag/')
subprocess.run(('git', 'tag', f'v{MACI_VERSION}', '-m', f"Release v{MACI_VERSION}")).check_returncode()
subprocess.run(('git', 'push', 'origin', f'v{MACI_VERSION}')).check_returncode()

cmd_output = subprocess.run(('git', 'tag', f'v{MACI_VERSION}', '-m', f"Release v{MACI_VERSION}"), capture_output=True)
cmd_output.check_returncode()

cmd_output = subprocess.run(('git', 'push', 'origin', f'v{MACI_VERSION}'), capture_output=True)
cmd_output.check_returncode()
os.chdir('..')

# Return to root code dir
os.chdir('..')

except BaseException as err_msg:
print('FAILED: maci deployment step...')
raise SystemExit(f'OUTPUT: {err_msg}') # exits 1
raise SystemExit(f'OUTPUT: {err_msg}\n{cmd_output.stderr.decode()}\n{cmd_output.stdout.decode()}') # exits 1

finally:
try:
Expand Down
4 changes: 2 additions & 2 deletions deploy/requirements-deploy.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
setuptools>=75.3.2 # tell safety latest ver being installed
wheel==0.45.1
twine==6.1.0
wheel>=0.45.1
twine==6.1.0
14 changes: 7 additions & 7 deletions src/maci/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def load(filename: _Union[str, _PathObj], *, attr_name_dedup: bool=True, encodin
Maci docs: https://docs.macilib.org
"""

def loaddict(filename: _Union[str, _PathObj], *, attr_name_dedup: bool=True, encoding: _Optional[str]=None) -> dict:
def loaddict(filename: _Union[str, _PathObj], *, attr_name_dedup: bool=False, encoding: _Optional[str]=None) -> dict:
"""
Loads maci (pythonic) data from a file

Expand Down Expand Up @@ -98,7 +98,7 @@ def loadstr(maci_str_data: str, *, attr_name_dedup: bool=True) -> _MaciDataObj:
Maci docs: https://docs.macilib.org
"""

def loadstrdict(maci_str_data: str, *, attr_name_dedup: bool=True) -> dict:
def loadstrdict(maci_str_data: str, *, attr_name_dedup: bool=False) -> dict:
"""
Loads maci (pythonic) data from a string

Expand Down Expand Up @@ -278,7 +278,7 @@ def build() -> _MaciDataObj:


### Hash Lib ###
def createfilehash(file_to_hash: _Union[str, _PathObj], file_to_store_hash: _Union[str, _PathObj, None], hash_algorithm: str='sha256', *, encoding: _Union[str, None]=None) -> str:
def createfilehash(file_to_hash: _Union[str, _PathObj], file_to_store_hash: _Union[str, _PathObj, None], hash_algorithm: str='sha256') -> str:
"""
Creates a hash of any file, and stores the hash data to a new created file

Expand All @@ -304,7 +304,7 @@ def createfilehash(file_to_hash: _Union[str, _PathObj], file_to_store_hash: _Uni
Maci docs: https://docs.macilib.org
"""

def comparefilehash(file_to_hash: _Union[str, _PathObj], stored_hash_file: _Union[str, _PathObj], hash_algorithm: str='sha256', *, encoding: _Union[str, None]=None) -> bool:
def comparefilehash(file_to_hash: _Union[str, _PathObj], stored_hash_file: _Union[str, _PathObj], hash_algorithm: str='sha256') -> bool:
"""
Compares a hash of any file by comparing the previously created file with hash data stored from using the "createfilehash" partner function

Expand Down Expand Up @@ -737,7 +737,7 @@ def xmlloadstrdict(xml_str_data: str) -> _OrderedDict[str, _Any]:
Maci docs: https://docs.macilib.org
"""

def xmldump(filename: _Union[str, _PathObj], data: _Union[_ElementTree, _Element], *, append: bool=False, encoding: _Union[str, None]=None) -> None:
def xmldump(filename: _Union[str, _PathObj], data: _Union[_ElementTree, _Element], *, append: bool=False, pretty: bool=True, full_doc: bool=True, encoding: _Union[str, None]=None) -> None:
"""
Dumps xml data to a file from xml etree ElementTree or Element object

Expand Down Expand Up @@ -765,7 +765,7 @@ def xmldumpdict(filename: _Union[str, _PathObj], data: _Dict[str, _Any], *, appe
Maci docs: https://docs.macilib.org
"""

def xmldumpstr(data: _Element, *, encoding: str='utf-8') -> str:
def xmldumpstr(data: _Element, *, pretty: bool=True, full_doc: bool=True, encoding: str='utf-8') -> str:
"""
Dumps xml data to a string from xml etree Element object

Expand Down Expand Up @@ -799,7 +799,7 @@ def xmldumpstrdict(data: _Dict[str, _Any], *, pretty: bool=True, full_doc: bool=

def xmlbuildmanual() -> _ModuleType:
"""
Returns an empty xml ElementTree module object to manually build xml etree data
Returns a xml ElementTree module to manually build xml etree data

Returns etree -> Module('xml')

Expand Down
8 changes: 3 additions & 5 deletions src/maci/_hash/comparefilehash.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

#########################################################################################################
# Compare file hashes
def comparefilehash(file_to_hash: _Union[str, _PathObj], stored_hash_file: _Union[str, _PathObj], hash_algorithm: str='sha256', *, encoding: _Union[str, None]=None) -> bool:
def comparefilehash(file_to_hash: _Union[str, _PathObj], stored_hash_file: _Union[str, _PathObj], hash_algorithm: str='sha256') -> bool:
"""
Compares a hash of any file by comparing the previously created file with hash data stored from using the "createfilehash" partner function

Expand Down Expand Up @@ -40,25 +40,23 @@ def comparefilehash(file_to_hash: _Union[str, _PathObj], stored_hash_file: _Unio
err_msg_hash_file = f"Only str is allowed for 'stored_hash_file'"
err_msg_str_hash = f"Only str is allowed for 'hash_algorithm'"
err_msg_hash = f"Invalid or no hash option chosen for 'hash_algorithm'"
err_msg_str_encoding = f"Only str|None or valid option is allowed for 'encoding'"

if not isinstance(file_to_hash, (str, _PathObj)): raise CompareFileHash(err_msg_str_file_src, f'"{file_to_hash}"')
if not isinstance(stored_hash_file, (str, _PathObj)): raise CompareFileHash(err_msg_hash_file, f'"{stored_hash_file}"')
if not isinstance(hash_algorithm, str): raise CompareFileHash(err_msg_str_hash, f'"{hash_algorithm}"')
if not hash_algorithm in ALGO_OPTIONS: raise CompareFileHash(err_msg_hash, f'"{hash_algorithm}"')
if not isinstance(encoding, (str, type(None))): raise CompareFileHash(err_msg_str_encoding, f'\nGot: {repr(encoding)}')

# Convert filenames to str to catch Path objects
file_to_hash = str(file_to_hash)
stored_hash_file = str(stored_hash_file)

# Collect hash data, then return result
try: _hash_data = _createfilehash(file_to_hash, None, hash_algorithm, encoding=encoding)
try: _hash_data = _createfilehash(file_to_hash, None, hash_algorithm)
except CreateFileHash as err_msg: raise CompareFileHash(err_msg)

try:
_stored_hash_data: _Any # ignore type checker
_stored_hash_data = _load(stored_hash_file, encoding=encoding)
_stored_hash_data = _load(stored_hash_file)
except Load as err_msg: raise CompareFileHash(err_msg)

return (_hash_data == _stored_hash_data.hash_data)
11 changes: 4 additions & 7 deletions src/maci/_hash/createfilehash.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#########################################################################################################
# Create file hash
def createfilehash(file_to_hash: _Union[str, _PathObj], file_to_store_hash: _Union[str, _PathObj, None], hash_algorithm: str='sha256', *, encoding: _Union[str, None]=None) -> str:
def createfilehash(file_to_hash: _Union[str, _PathObj], file_to_store_hash: _Union[str, _PathObj, None], hash_algorithm: str='sha256') -> str:
"""
Creates a hash of any file, and stores the hash data to a new created file

Expand Down Expand Up @@ -43,13 +43,11 @@ def createfilehash(file_to_hash: _Union[str, _PathObj], file_to_store_hash: _Uni
err_msg_file_dst = f"Only str|None is allowed for 'file_to_store_hash'"
err_msg_str_hash = f"Only str is allowed for 'hash_algorithm'"
err_msg_hash = f"Invalid or no hash option chosen for 'hash_algorithm'"
err_msg_str_encoding = f"Only str|None or valid option is allowed for 'encoding'"

if not isinstance(file_to_hash, (str, _PathObj)): raise CreateFileHash(err_msg_str_file_src, f'"{file_to_hash}"')
if not isinstance(file_to_store_hash, (str, _PathObj, type(None))): raise CreateFileHash(err_msg_file_dst, f'"{file_to_store_hash}"')
if not isinstance(hash_algorithm, str): raise CreateFileHash(err_msg_str_hash, f'"{hash_algorithm}"')
if not hash_algorithm in ALGO_OPTIONS: raise CreateFileHash(err_msg_hash, f'"{hash_algorithm}"')
if not isinstance(encoding, (str, type(None))): raise CreateFileHash(err_msg_str_encoding, f'\nGot: {repr(encoding)}')

# Convert filenames to str to catch Path objects
file_to_hash = str(file_to_hash)
Expand All @@ -65,19 +63,18 @@ def createfilehash(file_to_hash: _Union[str, _PathObj], file_to_store_hash: _Uni

# Read source file data and update hash
_readbytes: _Any # ignore type checker
try: _readbytes = _loadraw(file_to_hash)

try: _readbytes = _loadraw(file_to_hash, byte_data=True)
except LoadRaw as err_msg: raise CreateFileHash(err_msg)

try: _readbytes = _readbytes.encode() if encoding is None else _readbytes.encode(encoding=encoding)
except LookupError: raise CreateFileHash(err_msg_str_encoding, f'\nGot: {repr(encoding)}')
_hash_type.update(_readbytes)

# Store hash to file
_hash_type = _hash_type.hexdigest()

try:
if isinstance(file_to_store_hash, str):
_dumpraw(file_to_store_hash, f'hash_data = "{_hash_type}"', encoding=encoding)
_dumpraw(file_to_store_hash, f'hash_data = "{_hash_type}"')
except DumpRaw as err_msg: raise CreateFileHash(err_msg)

# Return hash data also
Expand Down
29 changes: 7 additions & 22 deletions src/maci/_native/loaddict.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

#########################################################################################################
# Import py Data from File
def loaddict(filename: _Union[str, _PathObj], *, attr_name_dedup: bool=True, encoding: _Optional[str]=None) -> _Optional[dict]:
def loaddict(filename: _Union[str, _PathObj], *, attr_name_dedup: bool=False, encoding: _Optional[str]=None) -> _Optional[dict]:
"""
Loads maci (pythonic) data from a file

Expand Down Expand Up @@ -55,35 +55,20 @@ def loaddict(filename: _Union[str, _PathObj], *, attr_name_dedup: bool=True, enc
'_assignment_hard_locked_atrribs_err_msg': "Attribute Name Hard Locked! Cannot be reassigned, deleted, or unlocked"
}

# Internal Key List to Remove from Dict
internal_remove_key_list = {
'_MaciDataObjConstructor__assignment_locked_attribs',
'_MaciDataObjConstructor__assignment_hard_locked_attribs' ,
'_MaciDataObjConstructor__assigned_src_reference_attr_map',
'_MaciDataObjConstructor__assigned_dst_reference_attr_map',
'_MaciDataObjConstructor__attrib_name_dedup',
'__maci_obj_format_id__',
'_MaciDataObjConstructor__assignment_locked_atrribs_err_msg',
'_MaciDataObjConstructor__assignment_hard_locked_atrribs_err_msg',
'_MaciDataObjConstructor__ignore_internal_maci_attr_check',
}

# Generate Dict as a Fresh Copy
try:
dict_data = _deepcopy(vars(_MaciDataObj(
maci_data = _MaciDataObj(
filename,
_is_load_request=True,
attr_name_dedup=attr_name_dedup,
encoding=encoding,
_ignore_internal_maci_attr_check=True,
**err_messages
)))
)
except Load as __err_msg: raise LoadDict(__err_msg) from None
except LookupError: raise LoadDict(err_msg_type_encoding, f'\nGot: {repr(encoding)}')

# Remove any Internal Keys
for remove_key in internal_remove_key_list:
if remove_key in dict_data: # pragma: no branch
del dict_data[remove_key]

# Return Final Import
# Return Import
dict_data = _deepcopy(maci_data._MaciDataObjConstructor__assignment_tracker)
del maci_data
return dict_data
29 changes: 7 additions & 22 deletions src/maci/_native/loadstrdict.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

#########################################################################################################
# Import py Data from String
def loadstrdict(maci_str_data: str, *, attr_name_dedup: bool=True) -> _Optional[dict]:
def loadstrdict(maci_str_data: str, *, attr_name_dedup: bool=False) -> _Optional[dict]:
"""
Loads maci (pythonic) data from a string

Expand Down Expand Up @@ -44,38 +44,23 @@ def loadstrdict(maci_str_data: str, *, attr_name_dedup: bool=True) -> _Optional[
'_assignment_hard_locked_atrribs_err_msg': "Attribute Name Hard Locked! Cannot be reassigned, deleted, or unlocked"
}

# Internal Key List to Remove from Dict
internal_remove_key_list = {
'_MaciDataObjConstructor__assignment_locked_attribs',
'_MaciDataObjConstructor__assignment_hard_locked_attribs' ,
'_MaciDataObjConstructor__assigned_src_reference_attr_map',
'_MaciDataObjConstructor__assigned_dst_reference_attr_map',
'_MaciDataObjConstructor__attrib_name_dedup',
'__maci_obj_format_id__',
'_MaciDataObjConstructor__assignment_locked_atrribs_err_msg',
'_MaciDataObjConstructor__assignment_hard_locked_atrribs_err_msg',
'_MaciDataObjConstructor__ignore_internal_maci_attr_check',
}

# Generate Dict as a Fresh Copy
try:
dict_data = _deepcopy(vars(_MaciDataObj(
maci_data = _MaciDataObj(
'',
_is_load_request=True,
_str_data=maci_str_data,
_is_str_parse_request=True,
attr_name_dedup=attr_name_dedup,
encoding=None,
_ignore_internal_maci_attr_check=True,
**__err_messages,
)))
)
except Load as __err_msg:
__err_msg.item = __err_msg.item.replace("\nFile: ''", "")
raise LoadStrDict(__err_msg.msg, f'{__err_msg.item}') from None

# Remove any Internal Keys
for remove_key in internal_remove_key_list:
if remove_key in dict_data: # pragma: no branch
del dict_data[remove_key]

# Return Final Import
# Return Import
dict_data = _deepcopy(maci_data._MaciDataObjConstructor__assignment_tracker)
del maci_data
return dict_data
2 changes: 1 addition & 1 deletion src/maci/_xml/xmlbuildmanual.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# Build manual xml data
def xmlbuildmanual() -> _ModuleType:
"""
Returns an empty xml ElementTree module object to manually build xml etree data
Returns a xml ElementTree module to manually build xml etree data

Returns etree -> Module('xml')

Expand Down
4 changes: 2 additions & 2 deletions src/maci/_xml/xmldump.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#########################################################################################################
# Export xml file
def xmldump(filename: _Union[str, _PathObj], data: _Union[_ElementTree, _Element], *, append: bool=False, encoding: _Union[str, None]=None) -> None:
def xmldump(filename: _Union[str, _PathObj], data: _Union[_ElementTree, _Element], *, append: bool=False, pretty: bool=True, full_doc: bool=True, encoding: _Union[str, None]=None) -> None:
"""
Dumps xml data to a file from xml etree ElementTree or Element object

Expand Down Expand Up @@ -44,6 +44,6 @@ def xmldump(filename: _Union[str, _PathObj], data: _Union[_ElementTree, _Element
if isinstance(data, _ElementTree):
data = data.getroot()

data_str = _xmldumpstr(data)
data_str = _xmldumpstr(data, pretty=pretty, full_doc=full_doc)
_dumpraw(filename, data_str, encoding=encoding, append=append)
except DumpRaw as err_msg: raise XmlDump(err_msg) from None
12 changes: 10 additions & 2 deletions src/maci/_xml/xmldumpstr.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# xmldumpstr
#########################################################################################################
# Imports
import sys
import xml.etree.ElementTree as _xml_etree # nosec: B405 # ignore sec checker - upto dev discretion to run provided maci._defuse_xml_stdlib()
from ..error import XmlDumpStr

#########################################################################################################
# Export xml str
def xmldumpstr(data: _xml_etree.Element, *, encoding: str='utf-8') -> str:
def xmldumpstr(data: _xml_etree.Element, *, pretty: bool=True, full_doc: bool=True, encoding: str='utf-8') -> str:
"""
Dumps xml data to a string from xml etree Element object

Expand All @@ -29,5 +30,12 @@ def xmldumpstr(data: _xml_etree.Element, *, encoding: str='utf-8') -> str:
if not isinstance(encoding, (str, type(None))): raise XmlDumpStr(err_msg_type_encoding, f'\nGot: {repr(encoding)}')

# Export Data
try: return _xml_etree.tostring(data).decode(encoding=encoding)
if (sys.version_info >= (3, 9)) and pretty: # pragma: no cover # etree indent only supported py39+
space_level = 4
_xml_etree.indent(data, space=" "*space_level)

encoding = sys.getdefaultencoding() if encoding is None else encoding

try:
return _xml_etree.tostring(data, encoding=encoding, xml_declaration=full_doc).decode(encoding=encoding)
except LookupError: raise XmlDumpStr(err_msg_type_encoding, f'\nGot: {repr(encoding)}')
Loading
Loading