예제 #1
0
class CompileFile(object):
    '''
    class for loading file text and compiling markstache tags within markdown and JSON files.
    accepts a file cache list of File classes.
    '''
    def __init__(self, file_path, root, file_extensions, valid_context_types):
        assert isinstance(file_path, str), \
            'expected file_path as str but got {}' \
            .format(type(file_path))
        assert isinstance(root, str), \
            'expected root as str but got {}' \
            .format(type(file_path))
        assert isinstance(file_extensions, dict), \
            'expected file_extension as dict but got {}' \
            .format(type(file_extensions))
        assert all([isinstance(val, list) for val in file_extensions.itervalues()]), \
            'file_extensions contains non-list elements: {}' \
            .format([type(e) for e in file_extensions])
        all_extensions = [
            ext for val in file_extensions.itervalues() for ext in val
        ]
        assert len(all_extensions) == len(set(all_extensions)), \
            'file_extensions contains shared extensions across file types'
        assert isinstance(valid_context_types, tuple), \
            'expected valid_context_types as tuple but got {}' \
            .format(type(valid_context_types))
        assert all([isinstance(type_element, type) for type_element in valid_context_types]), \
            'non-type type passed to valid_context_types'

        # self._file_cache = file_cache if file_cache else []
        self.file = None
        self.__path = os.path.abspath(os.path.join(root, file_path))
        self.context = {}
        self.staches = {'matches': [], 'renders': []}
        self.__re = {
            'stache':
            re.compile(r'{{.*}}'),
            'ref':
            re.compile(
                r'^{{[\s\t]*_ref[\s\t]*=[\s\t]*(\(.*\)|[\'\"].*[\'\"])[\s\t]*}}$'
            )
        }
        self.__file_extensions = file_extensions
        self.__valid_context_types = valid_context_types

    def process_file(self):
        '''
        processes File class based on whether it is a markdown file, JSON file, or other. In the
        first two cases, files are compiles based on markstache tags in file
        '''

        cache_paths = [f.path for f in FILE_CACHE.cache]
        if self.__path in cache_paths:
            return

        self._load_file()
        self._stache_search()
        if len(self.staches['matches']) == 0:
            self._finally()
            return
        self._compile()
        self._finally()

    def set_context(self, context=None):
        '''
        set the context dict for compilation
        '''

        assert isinstance(context, dict), \
            'expected context as dict but got {}' \
            .format(type(context))

        def _is_valid_type(var):
            '''
            check context dict recursively for invalid type in values
            '''

            if isinstance(var, dict):
                for key, val in var.iteritems():
                    if isinstance(val, dict):
                        _is_valid_type(val)
                    else:
                        assert isinstance(val, self.__valid_context_types), \
                            'invalid type passed in context:\n    {}: {}'.format(key, type(val))

        try:
            _is_valid_type(context)
            self.context = context
        except AssertionError as error:
            raise AssertionError, str(error)

    def _set_re(self, re_name, re_regex):
        assert isinstance(re_name, str), \
            'expected re_name as str but got {}' \
            .format(type(re_name))
        assert isinstance(re_regex, re._pattern_type), \
            'expected re_regex as regex object but got {}' \
            .format(type(re_regex))
        self.__re[re_name] = re_regex

    def _stache_search(self):
        self.staches['matches'] = self.__re['stache'].findall(
            self.file.contents)

    def _compile(self):
        for stache in self.staches['matches']:
            if re.match(self.__re['ref'], stache):
                self.staches['renders'].append('s')
            else:
                to_eval = stache.strip().strip('{}')
                compile_context = self.context.copy()
                try:
                    eval_val = str(eval(to_eval, compile_context))
                except NameError as error:
                    eval_val = ''
                    raise NameError, '{} in context dict'.format(str(error))
                self.staches['renders'].append(eval_val)
        for i in range(len(self.staches['matches'])):
            # if re.match(self.__re['ref'], (self.staches['matches'][i]):

            self.file.contents = re.sub(self.__re['stache'],
                                        self.staches['renders'][i],
                                        self.file.contents)

    def _load_file(self):
        self.file = File(self.__path)
        self.file.load()

    def _finally(self):
        FILE_CACHE.add_to_cache(self.file)