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:
parent
837d7a4703
commit
036dd24098
8
aif.xsd
8
aif.xsd
@ -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>
|
||||||
|
@ -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)
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
AIF-NG owes thanks to:
|
AIF-NG owes thanks to:
|
||||||
|
|
||||||
* jthan for being a good rubber ducky
|
* jthan for being a good rubber ducky
|
||||||
|
Loading…
Reference in New Issue
Block a user