"""Language
This module provides functions to load the different languages
parsers, writers and converters.
## Constants
`LEXOR_PATH`: The paths where lexor looks for the parsing, writing
and converting styles.
"""
import os
import sys
import site
import textwrap
from pkg_resources import parse_version
from os.path import splitext, abspath
from imp import load_source
from glob import iglob, glob
from lexor.command import config
DEFAULTS = {
'_': 'default',
'md': 'markdown',
'mdown': 'markdown',
'mkdn': 'markdown',
'mkd': 'markdown',
'mdwn': 'markdown',
'mdtxt': 'markdown',
'mdtext': 'markdown',
'text': 'markdown',
'lex': 'lexor',
'pyhtml': 'html',
'pyxml': 'xml',
}
DESC = """
Show the styles available to lexor through the current configuration
file. You should run this command whenever you create a new
configuration file so that lexor may select the available languages
for you without the need to reinstall the styles.
"""
try:
LEXOR_PATH = [
'%s/lib/lexor' % site.getuserbase(),
'%s/lib/lexor' % sys.prefix
]
except AttributeError:
LEXOR_PATH = [
'lib/lexor',
'%s/lib/lexor' % sys.prefix
]
if 'LEXORPATH' in os.environ:
LEXOR_PATH = os.environ['LEXORPATH'].split(':') + LEXOR_PATH
[docs]def add_parser(subp, fclass):
"""Add a parser to the main subparser. """
subp.add_parser('lang', help='see available styles',
formatter_class=fclass,
description=textwrap.dedent(DESC))
def _handle_kind(paths, cfg):
"""Helper function for _handle_lang. """
styles = dict()
if paths:
kind = os.path.basename(paths[0])
for path in paths:
tmp = [os.path.basename(ele) for ele in glob('%s/*.py' % path)]
for style in tmp:
index = style.find('-')
if index == -1:
continue
if style[:index] not in styles:
styles[style[:index]] = []
styles[style[:index]].append(style[index+1:-3])
if 'version' not in cfg:
cfg.add_section('version')
for style in styles:
key = '%s.%s' % (kind, style)
if key in cfg['version']:
ver = cfg['version'][key]
print ' [*] %s -> %s' % (style, ver)
else:
ver = max(styles[style], key=parse_version)
cfg['version'][key] = ver
print ' [+] %s -> %s' % (style, ver)
config.write_config(cfg)
def _handle_lang(path, cfg):
"""Helper function for run. """
for kind in path:
print ' %s:' % kind
_handle_kind(path[kind], cfg)
[docs]def run():
"""Run the command. """
paths = []
for base in LEXOR_PATH:
paths += glob('%s/*' % base)
path = dict()
cfg = config.read_config()
for loc in paths:
kind = os.path.basename(loc)
try:
name, kind = kind.split('.', 1)
except ValueError:
continue
if name not in path:
path[name] = dict()
if kind not in path[name]:
path[name][kind] = [loc]
else:
path[name][kind].append(loc)
for lang in path:
print '%s:' % lang
_handle_lang(path[lang], cfg)
print ''
def _get_info(cfg, type_, lang, style, to_lang=None):
"""Helper function for get_style_module. """
if style == '_':
style = 'default'
if lang in cfg['lang']:
lang = cfg['lang'][lang]
if to_lang:
if to_lang in cfg['lang']:
to_lang = cfg['lang'][to_lang]
key = '%s.%s.%s.%s' % (lang, type_, to_lang, style)
name = '%s.%s.%s/%s' % (lang, type_, to_lang, style)
modname = 'lexor-lang_%s_%s_%s_%s' % (lang, type_, to_lang, style)
else:
key = '%s.%s.%s' % (lang, type_, style)
name = '%s.%s/%s' % (lang, type_, style)
modname = 'lexor-lang_%s_%s_%s' % (lang, type_, style)
return key, name, modname
[docs]def get_style_module(type_, lang, style, to_lang=None):
"""Return a parsing/writing/converting module. """
cfg = config.get_cfg(['lang', 'develop', 'version'])
config.update_single(cfg, 'lang', DEFAULTS)
key, name, modname = _get_info(cfg, type_, lang, style, to_lang)
if 'develop' in cfg:
try:
path = cfg['develop'][key]
if path[0] != '/':
path = '%s/%s' % (config.CONFIG['path'], path)
return load_source(modname, path)
except (KeyError, IOError):
pass
versions = []
for base in LEXOR_PATH:
if 'version' in cfg:
try:
path = '%s/%s-%s.py' % (base, name, cfg['version'][key])
except KeyError:
versions += glob('%s/%s*.py' % (base, name))
path = '%s/%s.py' % (base, name)
else:
versions += glob('%s/%s*.py' % (base, name))
path = '%s/%s.py' % (base, name)
try:
return load_source(modname, path)
except IOError:
continue
try:
mod = load_source(modname, versions[0])
mod.VERSIONS = versions
return mod
except (IOError, IndexError):
raise ImportError("lexor module not found: %s" % name)
[docs]def load_mod(modbase, dirpath):
"""Return a dictionary containing the modules located in
`dirpath`. The name `modbase` must be provided so that each
module may have a unique identifying name. The result will be a
dictionary of modules. Each of the modules will have the name
"modbase_modname" where modname is a module in the directory."""
mod = dict()
for path in iglob('%s/*.py' % dirpath):
if 'test' not in path:
module = path.split('/')[-1][:-3]
modname = '%s_%s' % (modbase, module)
mod[module] = load_source(modname, path)
return mod
[docs]def load_aux(info):
"""Wrapper around load_mod for easy use when developing styles.
The only parameter is the dictionary `INFO` that needs to exist
with every style. `INFO` is returned by the init function in
the lexor module."""
dirpath = splitext(abspath(info['path']))[0]
if info['to_lang']:
modbase = 'lexor-lang_%s_converter_%s' % (info['lang'],
info['to_lang'])
else:
modbase = 'lexor-lang_%s_%s_%s' % (info['lang'],
info['type'],
info['style'])
return load_mod(modbase, dirpath)
[docs]def load_rel(path, module):
"""Load relative to a path. If path is the name of a file the
filename will be dropped. """
if not os.path.isdir(path):
path = os.path.dirname(os.path.realpath(path))
if '.py' in module:
module = module[1:-3]
fname = '%s/%s.py' % (path, module)
return load_source('load-rel-%s' % module, fname)
[docs]def map_explanations(mod, exp):
"""Helper function to create a map of msg codes to explanations
in the lexor language modules. """
if not mod:
return
for mod_name, module in mod.iteritems():
exp[mod_name] = dict()
codes = module.MSG.keys()
for index in xrange(len(module.MSG_EXPLANATION)):
sub = len(codes) - 1
while sub > -1:
code = codes[sub]
if code in module.MSG_EXPLANATION[index]:
del codes[sub]
exp[mod_name][code] = index
sub -= 1
if not codes:
break