Skip to content

Instantly share code, notes, and snippets.

@wichert
Created November 5, 2012 12:37
Show Gist options
  • Save wichert/4016981 to your computer and use it in GitHub Desktop.
Save wichert/4016981 to your computer and use it in GitHub Desktop.
Patternslib syntax converter
import argparse
import os
import re
import sys
from lxml import etree
def read(fn):
with open(fn, 'r') as input:
parser = etree.HTMLParser()
return etree.parse(input, parser)
SPACE = re.compile('\s+')
OLD_SHORTHAND_SYNTAX = re.compile(r'^(\s*\S+\s*;)+')
NAMED_ARGUMENT = re.compile(r'^\s*(\w+)\s*:\s*(.*)')
def warn(node, msg):
print >> sys.stderr, '%d: %s' % (node.sourceline, msg)
def update_parser_syntax(node):
for (attr, value) in node.attrib.items():
if not attr.startswith('data-pat-'):
continue
match = OLD_SHORTHAND_SYNTAX.match(value)
if match is None:
continue
parts = value.split(';')
new_parts = []
while parts:
part = parts.pop(0).strip()
if not part:
warn(node,
'Can not convert shorthand syntax with missing value')
return
if NAMED_ARGUMENT.match(part) is not None:
parts.insert(0, part)
break
new_parts.append(part)
shorthand = ' '.join(new_parts)
node.attrib[attr] = '; '.join([shorthand] + parts)
def update_depends(node):
classes = SPACE.split(node.attrib['class'])
new_classes = []
depends = []
action = 'and'
for cls in classes:
if cls.startswith('dependsType'):
action = cls[12:]
continue
elif not cls.startswith('dependsOn-'):
new_classes.append(cls)
continue
parts = cls[10:].split('-')
if len(parts) == 1 or (len(parts) == 2 and parts[1] == 'on'):
depends.append(parts[0])
continue
elif len(parts) == 2 and parts[1] == 'off':
depends.append('not %s' % parts[0])
continue
elif len(parts) == 3:
if parts[1].lower() == 'equals':
depends.append('%s=%s' % (parts[0], parts[2]))
continue
elif parts[1].lower() == 'notequals':
depends.append('%s!=%s' % (parts[0], parts[2]))
continue
warn(node, 'Do not know how to convert depends class %s' % cls)
new_classes.append(cls)
node.attrib['data-pat-depends'] = action.join(depends)
if 'pat-depends' not in new_classes:
new_classes.append('pat-depends')
node.attrib['class'] = ' '.join(new_classes)
def update_tooltip(node):
classes = SPACE.split(node.attrib.get('class', ''))
if 'pat-tooltip' not in classes:
node.attrib['class'] = ' '.join(classes + ['pat-tooltip'])
arguments = node.attrib.pop('data-tooltip').split('!')
new = []
for arg in arguments:
if arg in ['click', 'ajax']:
new.append(arg)
elif arg == 'forcePosition':
new.append('force')
elif arg.startswith('position='):
new.insert(0, arg[9:])
else:
warn(node, 'Unknown tooltip argument: %s' % arg)
return
node.attrib['data-pat-tooltip'] = ' '.join(new)
def update(root):
for node in root.iter():
update_parser_syntax(node)
if 'data-tooltip' in node.attrib:
update_tooltip(node)
cls = node.attrib.get('class', '')
if not cls:
continue
if 'dependsOn-' in cls:
update_depends(node)
def main():
parser = argparse.ArgumentParser(
description='Apply Patternslib syntax changes to HTML documents.')
parser.add_argument('-i', '--in-place', default=False, action='store_true',
help='Update documents in place')
parser.add_argument('html', nargs='+',
help='HTML document to update')
options = parser.parse_args()
for html in options.html:
tree = read(html)
update(tree)
if options.in_place:
fn = '%s~' % html
with open(fn, 'w') as output:
tree.write(output, encoding='utf-8')
os.rename(fn, html)
else:
tree.write(sys.stdout, encoding='utf-8')
if __name__ == '__main__':
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment