Package: maci-only
maciA Python-Styled Serialization Language & Thin Wrapper Librarymaci is an easy to use library for data serialization. It can parse native python data types from any plain file, which is safer than using an executable .py file for your stored or configuration data. There are useful language features built-in like creating realistic constants for your name/value pairs by locking them, mapping a name to another to follow its value similar to a pointer, and much more.Its focus is to reduce boilerplate by removing repetitive code implementation, like code written for common file handling, or common libraries used like JSON, YAML, TOML, etc. maci on its own is a pure Python-based library, and I've used variations of this library on projects for many companies and decided I wanted to make a robust and stable public version. It has made common needs less painful, and has solved simplicity in many ways. Hope it helps you🎓 tutorials & docs:quick start:tutorial videofull tutorials:all videosdocs:maci docschangelog:update historyreadmeinstallingbasic usage: macibasic usage: thin libsexceptions, hints, and built-in toolsperformancetesting & releaseprevious support🍨 install flavorsfull --> maci, standard library, and 3rd-party packagespipinstallmacistandard lib --> maci and standard library based packages onlypipinstallmaci-stdjust maci --> maci package onlypipinstallmaci-onlyback to top📖 basic usagemaciExample file "my.file" with maci (Python-styled) data# Example maci data "my.file"data1='my data'data2=1data3=[1,2,3]data4={'k1':1}data5=Truedata6=(1,2,3)data7={1,2,3}data8=1.0data9=Nonedata10=b'\ndata\n'Loadload maci data from filemaci_data=maci.load('my.file')maci_data.data1# access data with attr nameload raw data from fileraw_data=maci.loadraw('my.file')# returns string (default)load attributes names and their values back into your object from filemaci.loadattrs('my.file',my_obj)# loads in-placemy_obj.data4# access data in your object with attr nameload as dict data from filedict_data=maci.loaddict('my.file')dict_data['data3']# access data as dict key nameload maci data from stringmaci_data=maci.loadstr('data1 = "data"')maci_data.data1# access data with attr nameload as dict data from stringdict_data=maci.loadstrdict('data3 = "data"')dict_data['data3']# access data as dict key nameDumpdump data to file from maci object, dict, or your own object with attrsmaci.dump('my.file',maci_dataordict_dataormy_obj)# creates new file with data formatted as maci syntaxdump raw data to filemaci.dumpraw('my.file','my data')# creates new file with data raw as-is to filedump data to string from maci object, dict, or your own object with attrsstr_data=maci.dumpstr(maci_dataordict_dataormy_obj)# returns string with data formatted as maci syntaxBuildbuild maci data in codemaci_data=maci.build()maci_data.data1='my data'maci_data.data2=[1,2,3]maci_data.data3=1maci_data.data4=TrueIn-File Language Featuresmaci supports varying in-file features. Here are some examples using a file named "my.file":Lock an attr from re-assignment using a lock glyph# Example maci data in "my.file"data1+l='my data'Hard Lock an attr from re-assignment, deletion, and unlocking using a hard lock glyph# Example maci data in "my.file"data1+h='my data'Reference and follow another attr's value with an attr (like a pointer) using a map glyph# Example maci data in "my.file"data1='my data'data2+m=data1Date and time parsing# Example maci data in "my.file"# Multiple options -> returns datetime, date, or time objectdate_time1=2023-03-1322:06:00date_time2=2023-03-1322:06:00.50time_date1=22:06:002023-03-13time_date2=22:06:00.502023-03-13time1=22:06:00time2=22:06:00.50date=2023-03-13date_time_iso8601=2023-03-13T22:06:00In-Code Language FeaturesThe in-file language features can also be handled in code with a maci objectmaci_data.lock_attr('data1')maci_data.hard_lock_attr('data2')maci_data.map_attr('data3','data4')You may unlock attrs, unmap attrs, and much more with a maci objectNote: if you dump your maci object back to a file, all language features will be retained and represented appropriately in the fileback to top📖 basic usage: thin libsjson -> based onjson standard libraryload json data from filedata=maci.jsonload('file.json')load json data from stringdata=maci.jsonloadstr('{"k1": "data"}')dump python data to file as json datamaci.jsondump('file.json',data)dump data to string as json datajson_data=maci.jsondumpstr(data)yaml -> based onpyyaml frameworkload yaml data from filedata=maci.yamlload('file.yaml')load yaml data from stringdata=maci.yamlloadstr('k1: data')dump python data to file as yaml datamaci.yamldump('file.yaml',data)dump data to string as yaml datayaml_data=maci.yamldumpstr(data)There are also "loadall" and "dumpall" for multiple yaml docs in a filetoml -> based ontomli librariesload toml data from filedata=maci.tomlload('file.toml')load toml data from stringdata=maci.tomlloadstr('data1 = "data1"')dump python data to file as toml datamaci.tomldump('file.toml',data)dump data to string as toml datatoml_data=maci.tomldumpstr(data)ini -> based onconfigparser standard libraryload ini data from fileconfigparser_data=maci.iniload('file.ini')dump configparser data to file as ini datamaci.inidump('file.ini',configparser_data)build ini data to configparser data automatically - learn more aboutconfigparser objectsconfigparser_data=maci.inibuildauto({'section1':{'k1':'value1'}})build configparser data manually - learn more aboutconfigparser objectsconfigparser_data=maci.inibuildmanual()xml -> based onxmltodict module & xml etree from standard libraryDict (easiest)load xml data from file as dictdict_data=maci.xmlloaddict('file.xml')load xml data from string as dictdict_data=maci.xmlloadstrdict('<tag>data</tag>')dump dict data to file as xml datamaci.xmldumpdict('file.xml',dict_data)dump dict data to string as xml dataxml_data=maci.xmldumpstrdict(dict_data)ElementTree - learn more aboutelement tree objectsload xml data from file as element tree objectet_data=maci.xmlload('file.xml')load xml data from string as element tree objectet_data=maci.xmlloadstr('<tag>data</tag>')dump element tree data to file as xml datamaci.xmldump('file.xml',et_data)dump element tree data to string as xml dataxml_data=maci.xmldumpstr(et_data)build element tree data manuallyet_data=maci.xmlbuildmanual()back to top🪄 helpful extrasexceptionsAll exceptions/errors thrown by maci and its thin wrapper libraries are conveniently accessible here:maci.errorExamples of different load exceptionsmaci.error.Loadmaci.error.JsonLoadmaci.error.YamlLoadmaci.error.TomlLoadTo catch/suppress all maci exceptions, use its base exceptionmaci.error.MaciErrorhintingFor type hinting/annotation needs, you can conveniently access the respective object types here:maci.hintExamples of different hint objectsmaci.hint.MaciDataObjmaci.hint.ConfigParsermaci.hint.ElementTreemaci.hint.Elementuseful toolscleanformatformat nested data cleanlystr_data=maci.cleanformat([1,{'k1':1,'k2':2},2])print(str_data)Output-->[1,{'k1':1,'k2':2,},2,]picklingpickle your objects using a non-executable file concept with maci# Dump to filemaci_data.pickle_data=maci.pickledumpbytes(my_obj)maci.dump('my.data',maci_data)# Load back from filemaci_data=maci.load('my.data')my_obj=maci.pickleloadbytes(maci_data.pickle_data)This is better than having your whole file having the ability to be unpickled, especially if you cannot trust the file's integrity. More on this  frompython pickle docs. Though this may help improve pickling needs, still use methods to verify integrity of your pickled data if requiredhashingEasily generate hash of a file and store hash - default hash is sha256maci.createfilehash('my.data','my.data.hashed')# always returns string of file hashNow simply compare the hash of the source file to check integrity when neededmaci.comparefilehash('my.data','my.data.hashed')# returns bool if hash is a valid matchCreate hash of data - default hash is sha256maci.createhash('data')# returns string of hashback to top⏳️ performancePerformance tests each library loading100,000 lines of dataeach in their natural usageTests are done by loading a file with 100 lines of data 1000 times with the proper file syntax for each library. You may also consider this test about loading 1000 files within the time taken as wellResults vary based on system spec, but you may simulate or prove the same difference in test results for your needs from the "perf" dir in this repo. Results below is running the test 3 times consecutivelylibs tested:json, pyyaml, tomli, xmltodict, maciNotesXML ElementTree type and INI Configparser tests were left out for nowpyyaml loads much faster using its c-based safe loader, but using the native out of the box methods/functions provided as tests for fairness and potential compatibility issues for needing LibYAML bindings# Test 1
$ python3 perf_load.py 
Performance tests: "load" - loading file 1000 times with 100 lines of data

xml: 0.225348
json: 0.016725
yaml: 3.625997
toml: 0.23937
maci: 0.807448

# Test 2
$ python3 perf_load.py 
Performance tests: "load" - loading file 1000 times with 100 lines of data

xml: 0.22595
json: 0.016566
yaml: 3.652053
toml: 0.242974
maci: 0.806545

# Test 3
$ python3 perf_load.py 
Performance tests: "load" - loading file 1000 times with 100 lines of data

xml: 0.225579
json: 0.01695
yaml: 3.611955
toml: 0.239593
maci: 0.802843placelib🥇 1stjson - avg 0.016s🥈 2ndxmltodict - avg 0.225s🥉 3rdtomli - avg 0.240s4thmaci - avg 0.805s5thpyyaml - avg 3.630s (4th if using CLoader)Current differences in load time results for 100k lines of data from maci compared to popular or modern librariesLooking to continually improve maci's performance and update the results, but so far, not bad for pure python.back to top🚀 testing & release300+ tests and counting ⚡️A maci release is only deployed/released if all qa tests pass, and if the revision number is incremented.All coverage testing must be at 100% or test pipeline will fail (badge is not auto-updated, and just indicates confidence in testing at 100%).back to top⏪ previous project supportProject maci is derived from an older project calledsfcparsethat is no longer supported, and still provides forward ported support for most of the older API names as a courtesy. sfcparse uses the MIT license, and therefore, maci does not really need to associate itself with that older project, but out of notice for the reason of having the forward ported support is it being mentioned if desiring to migrate.Reason for sfcparse's deprecation was merely for desire of re-branding and scrapping the old to make usage simpler and anew, thus, maci.Though maci does support the older API names as a courtesy, some names being attempted to use may throw exceptions. Also, functionality in a lot of the forward connected API names may require different parameter positional args or kwargs. See these files for API matched names and where they point tofunction names:__init__.pyunder __getattr__exception names:error.pyunder __getattr__back to top