gorram it pycharm, stop warning me about this gorram dollar sign. it doesn't need to and shouldn't be escaped in XSD pattern expressions.

This commit is contained in:
brent s 2019-10-28 14:11:35 -04:00
parent 837d7a4703
commit 036dd24098
3 changed files with 71 additions and 38 deletions

View File

@ -147,10 +147,10 @@
<xs:simpleType name="t_shadowhash"> <xs:simpleType name="t_shadowhash">
<!-- http://man7.org/linux/man-pages/man3/crypt.3.html#NOTES --> <!-- http://man7.org/linux/man-pages/man3/crypt.3.html#NOTES -->
<xs:restriction base="xs:token"> <xs:restriction base="xs:token">
<xs:pattern value="(\$1)?(\$[a-zA-Z0-9./]{1,16})\$[a-zA-Z0-9./]{22}"/><!-- md5 --> <xs:pattern value="($1)?($[a-zA-Z0-9./]{1,16})$[a-zA-Z0-9./]{22}"/><!-- md5 -->
<xs:pattern value="(\$2[abxy]?)?(\$[0-9]+)\$[a-zA-Z0-9./]{53}"/><!-- Blowfish --> <xs:pattern value="($2[abxy]?)?($[0-9]+)$[a-zA-Z0-9./]{53}"/><!-- Blowfish -->
<xs:pattern value="(\$5)?(\$[a-zA-Z0-9./]{1,16})\$[a-zA-Z0-9./]{43}"/><!-- sha256 --> <xs:pattern value="($5)?($[a-zA-Z0-9./]{1,16})$[a-zA-Z0-9./]{43}"/><!-- sha256 -->
<xs:pattern value="(\$6)?(\$[a-zA-Z0-9./]{1,16})\$[a-zA-Z0-9./]{86}"/><!-- sha512 --> <xs:pattern value="($6)?($[a-zA-Z0-9./]{1,16})$[a-zA-Z0-9./]{86}"/><!-- sha512 -->
<xs:whiteSpace value="collapse"/> <xs:whiteSpace value="collapse"/>
</xs:restriction> </xs:restriction>
</xs:simpleType> </xs:simpleType>

View File

@ -4,27 +4,29 @@ import re
import requests import requests
from lxml import etree from lxml import etree


# https://stackoverflow.com/questions/30232031/how-can-i-strip-namespaces-out-of-an-lxml-tree/30233635#30233635 ?

_patterns = {'raw': re.compile(r'^\s*(?P<xml><(\?xml|aif)\s+.*)\s*$', re.DOTALL|re.MULTILINE), _patterns = {'raw': re.compile(r'^\s*(?P<xml><(\?xml|aif)\s+.*)\s*$', re.DOTALL|re.MULTILINE),
'remote': re.compile(r'^(?P<uri>(?P<proto>(https?|ftps?)://)(?P<path>.*))\s*$'), 'remote': re.compile(r'^(?P<uri>(?P<proto>(https?|ftps?)://)(?P<path>.*))\s*$'),
'local': re.compile(r'^(file://)?(?P<path>(/?[^/]+)+/?)$')} 'local': re.compile(r'^(file://)?(?P<path>(/?[^/]+)+/?)$')}



class Config(object): class Config(object):
def __init__(self, *args, **kwargs): def __init__(self, xsd_path = None, *args, **kwargs):
self.xsd_path = None
self.tree = None self.tree = None
self.namespaced_tree = None self.namespaced_tree = None
self.xml = None self.xml = None
self.namespaced_xml = None self.namespaced_xml = None
self.raw = None self.raw = None
self.xsd = None self.xsd = None
self.defaultsParser = None


def main(self, validate = True): def main(self, validate = True, populate_defaults = True):
self.fetch() self.fetch()
self.parseRaw() self.parseRaw()
if populate_defaults:
self.populateDefaults()
if validate: if validate:
self.validate() self.validate()

return() return()


def fetch(self): # Just a fail-safe; this is overridden by specific subclasses. def fetch(self): # Just a fail-safe; this is overridden by specific subclasses.
@ -32,6 +34,8 @@ class Config(object):
return() return()


def getXSD(self, xsdpath = None): def getXSD(self, xsdpath = None):
if not xsdpath:
xsdpath = self.xsd_path
raw_xsd = None raw_xsd = None
if xsdpath: if xsdpath:
xsdpath = os.path.abspath(os.path.expanduser(xsdpath)) xsdpath = os.path.abspath(os.path.expanduser(xsdpath))
@ -45,20 +49,41 @@ class Config(object):
schemaLocation = '{{{0}}}schemaLocation'.format(xsi) schemaLocation = '{{{0}}}schemaLocation'.format(xsi)
schemaURL = self.xml.attrib.get(schemaLocation, schemaURL = self.xml.attrib.get(schemaLocation,
'https://aif-ng.io/aif.xsd?ref={0}'.format(self.xml.attrib['version'])) 'https://aif-ng.io/aif.xsd?ref={0}'.format(self.xml.attrib['version']))
split_url = schemaURL.split()
if len(split_url) == 2: # a properly defined schemaLocation
schemaURL = split_url[1]
else:
schemaURL = split_url[0]
req = requests.get(schemaURL) req = requests.get(schemaURL)
if not req.ok(): if not req.ok:
# TODO: logging!
raise RuntimeError('Could not download XSD') raise RuntimeError('Could not download XSD')
raw_xsd = req.content raw_xsd = req.content
self.xsd = etree.XMLSchema(etree.XML(raw_xsd)) self.xsd = etree.XMLSchema(etree.XML(raw_xsd))
return() return()


def parseRaw(self): def parseRaw(self, parser = None):
self.tree = etree.parse(self.raw) # self.xml = etree.parse(self.raw, parser = parser)
self.namespaced_tree = etree.parse(self.raw) self.xml = etree.fromstring(self.raw, parser = parser)
self.xml = self.tree.getroot() # self.namespaced_xml = etree.parse(self.raw, parser = parser)
self.namespaced_xml = self.namespaced_tree.getroot() self.namespaced_xml = etree.fromstring(self.raw, parser = parser)
self.tree = self.xml.getroottree()
self.namespaced_tree = self.namespaced_xml.getroottree()
self.tree.xinclude() self.tree.xinclude()
self.namespaced_tree.xinclude() self.namespaced_tree.xinclude()
self.stripNS()
return()

def populateDefaults(self):
if not self.xsd:
self.getXSD()
if not self.defaultsParser:
self.defaultsParser = etree.XMLParser(schema = self.xsd, attribute_defaults = True)
self.parseRaw(parser = self.defaultsParser)
return()

def removeDefaults(self):
self.parseRaw()
return() return()


def stripNS(self): def stripNS(self):
@ -71,31 +96,34 @@ class Config(object):
def validate(self): def validate(self):
if not self.xsd: if not self.xsd:
self.getXSD() self.getXSD()

self.xsd.assertValid(self.tree)
self.xsd.assertValid(self.namespaced_tree)
return() return()




class LocalFile(Config): class LocalFile(Config):
def __init__(self, path, *args, **kwargs): def __init__(self, path, xsd_path = None, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(xsd_path = xsd_path, *args, **kwargs)
self.source = ['local', _patterns['local'].search(path).group('path')] self.type = 'local'
self.source = _patterns['local'].search(path).group('path')


def fetch(self): def fetch(self):
self.source[1] = os.path.abspath(os.path.expanduser(self.source[1])) self.source = os.path.realpath(self.source)
if not os.path.isfile(self.source[1]): if not os.path.isfile(self.source):
raise ValueError('{0} does not exist'.format(self.source[1])) raise ValueError('{0} does not exist'.format(self.source))
with open(self.source[1], 'rb') as fh: with open(self.source, 'rb') as fh:
self.raw = fh.read() self.raw = fh.read()
return() return()




class RemoteFile(Config): class RemoteFile(Config):
def __init__(self, uri, *args, **kwargs): def __init__(self, uri, xsd_path = None, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.source = ('remote', uri) self.type = 'remote'
self.source = uri


def fetch(self): def fetch(self):
r = requests.get(self.source[1]) r = requests.get(self.source)
if not r.ok(): if not r.ok():
raise RuntimeError('Could not download XML') raise RuntimeError('Could not download XML')
self.raw = r.content self.raw = r.content
@ -103,36 +131,39 @@ class RemoteFile(Config):




class ConfigStr(Config): class ConfigStr(Config):
def __init__(self, rawxml, *args, **kwargs): def __init__(self, rawxml, xsd_path = None, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.source = ('raw', rawxml) self.type = 'raw_str'
self.source = rawxml


def fetch(self): def fetch(self):
self.raw = self.source[1].encode('utf-8') self.raw = self.source.encode('utf-8')
return() return()



class ConfigBin(Config): class ConfigBin(Config):
def __init__(self, rawbinaryxml, *args, **kwargs): def __init__(self, rawbinaryxml, xsd_path = None, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.source = ('raw_binary', rawbinaryxml) self.type = 'raw_bin'
self.source = rawbinaryxml


def fetch(self): def fetch(self):
self.raw = self.source[1] self.raw = self.source
return() return()




def getConfig(cfg_ref, validate = True): def getConfig(cfg_ref, validate = True, populate_defaults = True, xsd_path = None):
cfgobj = None cfgobj = None
# This is kind of gross. # This is kind of gross.
for configtype, pattern in _patterns.items(): for configtype, pattern in _patterns.items():
try: try:
if pattern.search(cfg_ref): if pattern.search(cfg_ref):
if configtype == 'raw': if configtype == 'raw':
cfgobj = ConfigStr(cfg_ref) cfgobj = ConfigStr(cfg_ref, xsd_path = xsd_path)
elif configtype == 'remote': elif configtype == 'remote':
cfgobj = RemoteFile(cfg_ref) cfgobj = RemoteFile(cfg_ref, xsd_path = xsd_path)
elif configtype == 'local': elif configtype == 'local':
cfgobj = LocalFile(cfg_ref) cfgobj = LocalFile(cfg_ref, xsd_path = xsd_path)
if cfgobj: if cfgobj:
break break
except TypeError: except TypeError:
@ -140,6 +171,8 @@ def getConfig(cfg_ref, validate = True):
if not ptrn.search(cfg_ref): if not ptrn.search(cfg_ref):
raise ValueError('Received junk data for cfg_ref') raise ValueError('Received junk data for cfg_ref')
else: else:
cfgobj = ConfigBin(cfg_ref) cfgobj = ConfigBin(cfg_ref, xsd_path = xsd_path)
break break
if cfgobj:
cfgobj.main(validate = validate, populate_defaults = populate_defaults)
return(cfgobj) return(cfgobj)