def _update_config(self): self.config = deepcopy(self.defaults) logger.debug('>> Searching for config') for ext in ('.yml', '.yaml'): f = File(normpath(self.src.path, 'config' + ext)) if f.exists: logger.debug('.. found: %s', f.path) try: self.config.update(Config(f.content)) except ConfigException as e: raise ConfigException(e.message, 'src: {0}'.format(f.path)) self.config['locale'] = self.opts.get('locale', self.config['locale']) self.config['assets_url'] = absurl(self.config['assets_url'], '') self.config['base_url'] = absurl(self.opts.get('base_url', self.config['base_url']), '') for setting in ('archives_url', 'posts_url', 'tags_url'): self.config[setting] = absurl(self.config[setting]) for setting in ('archives_url', 'assets_url', 'base_url', 'posts_url', 'tags_url'): if re.search(r'(?:^\.{2}/|/\.{2}$|/\.{2}/)', self.config[setting]): raise ConfigException('Invalid config setting.', 'setting: {0}'.format(setting), 'path traversal is not allowed') for pattern in self.config['include']: if op.commonprefix((self.src.path, normpath(self.src.path, pattern))) != self.src.path: raise ConfigException('Invalid include path.', 'path: {0}'.format(pattern), 'path traversal is not allowed') break else: logger.debug('.. no config file found')
def _get_path(self, url): parts = [self.dest.path] + url.split('/') if url.endswith('/'): parts.append('index.html') return normpath(*parts)
def _parse(self): logger.info('>> Parsing') path = Directory(normpath(self.src.path, '_posts')) logger.debug('.. src: %s', path) for i, f in enumerate(path): post = Post(f) content = self.parser.parse(self.renderer.from_string(post.bodymatter, post.frontmatter)) excerpt = re.search(r'\A.*?(?:<p>(.+?)</p>)?', content, re.M | re.S).group(1) try: data = { 'content': content, 'date': post.date.strftime(self.config['date_format']).decode('utf-8'), 'excerpt': excerpt, 'tags': [], 'timestamp': timegm(post.date.utctimetuple()), 'url': self._get_post_url(post.date, post.slug), 'prev': False, 'next': False } except PageException: raise PageException('Invalid post slug.', 'src: {0}'.format(post.path)) data.update(post.frontmatter) if len(data['tags']) == 0: data['tags'].append('_untagged') else: data['tags'].sort() data['tags'] = [tag.title() for tag in data['tags']] data['title'] = str(data['title']) if post.draft: data['url'] = '/drafts/' + self._slugify(post.slug) self.drafts.append(data) else: self.posts.append(data) for tag in data['tags']: if tag not in self.tags: self.tags[tag] = [] self.tags[tag].append(data) self.posts.sort(key = lambda post: post['timestamp'], reverse = True) for post in self.posts: if self.posts.index(post) != len(self.posts) - 1: prev = self.posts[self.posts.index(post) + 1] post['prev'] = {'title': prev['title'], 'url': prev['url']} if self.posts.index(post) != 0: next = self.posts[self.posts.index(post) - 1] post['next'] = {'title': next['title'], 'url': next['url']}
def _generate(self): logger.debug('>> Initializing\n.. src: %s\n.. dest: %s', self.src.path, self.dest.path) self._update_config() if self.config['locale']: try: locale.setlocale(locale.LC_ALL, (self.config['locale'], 'utf-8')) except locale.Error: raise ConfigException('Locale not available.', 'run `locale -a` to see available locales') self.renderer.register({'site': self.config}) self._render() logger.info('>> Generating') assets_src = Directory(normpath(self.src.path, '_assets')) assets_dest = Directory(normpath(self.dest.path, *self.config['assets_url'].split('/'))) if self.dest.exists: if self.opts['force']: self.dest.empty() else: self.dest.rm() else: self.dest.mk() for page in self.pages: page.mk() assets_src.cp(assets_dest.path) for pattern in self.config['include']: for path in iglob(normpath(self.src.path, pattern)): dest = path.replace(self.src.path, self.dest.path) if op.isdir(path): Directory(path).cp(dest, False) elif op.isfile(path): File(path).cp(dest) logger.info('Completed in %.3fs', time() - self._start)
def __iter__(self): for root, dirs, files in walk(self.path): for d in dirs[:]: if d.startswith(('.', '_')): dirs.remove(d) for f in files: if f.startswith(('.', '_')): continue yield File(normpath(root, f))
def init(self): self.src = Directory(self._get_theme(self.opts['theme'])) self.dest = Directory(self.opts['dest']) if not self.src.exists: raise OptionException('Theme not found.') elif self.dest.exists and not self.opts['force']: raise OptionException('Destination already exists.', 'the -f flag must be passed to force initialization by deleting the destination') logger.info('>> Initializing') if self.opts['bare']: self.dest.rm() for d in ('_assets/css', '_assets/images', '_assets/js', '_templates', '_posts'): Directory(normpath(self.dest.path, d)).mk() File(normpath(self.dest.path, 'config.yml')).mk() else: self.src.cp(self.dest.path, False) logger.info('Completed in %.3fs', time() - self._start)
def setup(self): self.config.update(self.options) self.config['loader'] = _PrefixLoader(OrderedDict([ (op.sep, FileSystemLoader(self.path)), ('', FileSystemLoader(normpath(self.path, '_templates'))) ]), None) self.environment = Environment(**self.config) self.environment.filters['absolutize'] = self._absolutize self.environment.filters['date'] = self._date self.environment.globals.update(self.globals) self.environment.globals['get_asset'] = self._get_asset self.environment.globals['get_url'] = self._get_url if 'extensions' in self.config and 'jinja2.ext.i18n' in self.config['extensions']: try: langs = [locale.getlocale(locale.LC_MESSAGES)[0].decode('utf-8')] except AttributeError: langs = None self.environment.install_gettext_translations(gettext.translation(gettext.textdomain(), normpath(self.path, '_locales'), langs, fallback = True))
def _render(self): self._process() logger.info('>> Rendering') self.renderer.register({ 'archives': self.archives, 'posts': self.posts, 'tags': self.tags }) logger.debug('.. posts') for post in self.posts: try: self.pages.append(Page( self._get_path(post['url']), self._pygmentize(self.renderer.render(post['layout'], {'post': post})) )) except RendererException as e: raise RendererException(e.message, '{0} in post \'{1}\''.format(post['layout'], post['title'])) logger.debug('.. drafts') for draft in self.drafts: try: self.pages.append(Page( self._get_path(draft['url']), self._pygmentize(self.renderer.render(post['layout'], {'post': post})) )) except RendererException as e: raise RendererException(e.message, '{0} in draft\'{1}\''.format(draft['layout'], draft['title'])) logger.debug('.. pages') for f in self.src: if f.extension not in ('.html', '.htm', '.xml'): continue template = f.path.replace(self.src.path, '') self.pages.append(Page( normpath(self.dest.path, template), self._pygmentize(self.renderer.render(template)) )) if self.config['tag_layout'] and self.tags: logger.debug('.. tags') for name, data in self.tags: self.pages.append(Page( self._get_path(data['url']), self._pygmentize(self.renderer.render(self.config['tag_layout'], {'tag': data})) )) if self.config['archive_layout'] and self.archives: logger.debug('.. archives') for year, data in self.archives: self.pages.append(Page( self._get_path(data['url']), self._pygmentize(self.renderer.render(self.config['archive_layout'], {'archive': data})) ))