"""Simple parsing of a dynamic definition langauge (DDL) and dynamicmanipulation language (DML) files."""__author__='Paul Landes'fromtypingimportTuple,Dict,List,Iterable,ClassVarimportloggingimportreimportitertoolsasitfrompathlibimportPathfromzensols.persistimportpersistedlogger=logging.getLogger(__name__)
[docs]classDynamicDataParser(object):"""Parse a DDL/DML file meant also for prototyping. For example the file:: -- meta=init_sections=create_tables,create_idx -- name=create_idx create index person_name on person(name); -- name=create_tables create table person (id int, name text, age int); Would have ``create_idx`` and ``create_tables`` as sections and meta data:: {'init_sections': 'create_tables,create_idx'} """COMMENT_PAT:ClassVar[re.Pattern]=re.compile(r'^--.*')SEC_START_PAT:ClassVar[re.Pattern]=re.compile(r'^-- name=([a-zA-Z0-9_]+)')META_PAT:ClassVar[re.Pattern]=re.compile(r'^-- meta=([a-zA-Z0-9_]+)=(.+)$')
[docs]def__init__(self,dd_path:Path):"""Initialize. :param dd_path: the path of the file to parse """self.dd_path=dd_path
def_map_section_content(self,lines:List[str])->str:return'\n'.join(lines)@persisted('__parse')def_parse(self)->Tuple[Dict[str,str],Dict[str,str]]:iflogger.isEnabledFor(logging.INFO):logger.info(f'parsing {self.dd_path}')secs:List[str,Tuple[str,List[str]]]=[]sec_content:List[str]=[]meta:Dict[str,str]={}withopen(self.dd_path)asf:line:strforlineinf.readlines():line=line.rstrip()iflen(line)==0:continueifre.match(self.COMMENT_PAT,line):iflogger.isEnabledFor(logging.DEBUG):logger.debug(f'matched comment: {line}')sec_start=re.match(self.SEC_START_PAT,line)meta_match=re.match(self.META_PAT,line)sec_content=[]ifsec_startisnotNone:name=sec_start.group(1)secs.append((name,sec_content))elifmeta_matchisnotNone:meta[meta_match.group(1)]=meta_match.group(2)else:sec_content.append(line)sections={x[0]:self._map_section_content(x[1])forxinsecs}returnsections,meta@propertydefsections(self)->Dict[str,str]:"""Return the sections of the file. """returnself._parse()[0]@propertydefmetadata(self)->Dict[str,str]:"""Return the meta data found int he parse object. """returnself._parse()[1]
[docs]defget_init_db_sqls(self)->Iterable[str]:"""Return the set of statements that create all DB objects needed to fully CRUD. """init_secs=self.metadata['init_sections']secs=init_secs.split(',')entries=map(lambdax:self.sections[x],secs)sts=map(lambdax:re.split(';[ \t\n]*',x,flags=re.MULTILINE),entries)returnfilter(lambdax:len(x)>0,it.chain(*sts))