def __init__(self, string): super(Config, self).__init__() try: self.update(yaml.load(string)) except yaml.YAMLError: raise ConfigException('Config contains unsupported YAML.') except: raise ConfigException('Invalid config format.')
def _parse(self, container): for f in container.path: Timer.start() item = Item(f.path) try: frontmatter, bodymatter = re.search( r'\A---\s+^(.+?)$\s+---\s*(.*)\Z', f.content, re.M | re.S).groups() frontmatter = Config(frontmatter) except AttributeError: raise ContentException('Invalid frontmatter.', 'src: {0}'.format(f.path), 'frontmatter must not be empty') except ConfigException: raise ConfigException('Invalid frontmatter.', 'src: {0}'.format(f.path), 'fontmatter contains invalid YAML') if 'layout' not in frontmatter: raise ContentException('Invalid frontmatter.', 'src: {0}'.format(f.path), 'layout must be set') parser = self._get_parser( f, frontmatter.get('parser', container.config.get('parser', None))) slug, date = self._parse_filename(f) content = parser.parse( self._writer.from_string(bodymatter, frontmatter)) item['content'] = content item['date'] = date.strftime( self.site['date_format']).decode('utf-8') item['excerpt'] = re.search(r'\A.*?(?:<p>(.+?)</p>)?', content, re.M | re.S).group(1) item['tags'] = [] item['timestamp'] = timegm(date.utctimetuple()) item.update(frontmatter) item['url'] = self._get_content_url(container.config['url'], slug, date, frontmatter) container.add(item) logger.debug('.. (%.3fs) %s', Timer.stop(), f.path.replace(self.src.path, '')) container.sort() container.tag() container.archive() return container
def __init__(self, string): super(Config, self).__init__() try: self.update(yaml.load(string)) except yaml.YAMLError: raise ConfigException('Config contains unsupported YAML.') except: logger.debug('.. config file is empty') pass
def _get_path(self, url): parts = [self.dest.path] + url.split('/') if url.endswith('/'): parts.append('index.html') path = normpath(*parts) if op.commonprefix((self.dest.path, path)) != self.dest.path: raise ConfigException('Invalid URL.', 'url: {0}'.format(url), 'path traversal is not allowed') return path
def _initialize(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.writer.register({'site': self.config})
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.writer.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)
def _parse_item(self, config, f, simple = False): Timer.start() item = Item(f.path) try: frontmatter, bodymatter = re.search(r'\A---\s+^(.+?)$\s+---\s*(.*)\Z', f.content, re.M | re.S).groups() frontmatter = Config(frontmatter) except AttributeError: raise ContentException('Invalid frontmatter.', 'src: {0}'.format(f.path), 'frontmatter must not be empty') except ConfigException: raise ConfigException('Invalid frontmatter.', 'src: {0}'.format(f.path), 'fontmatter contains invalid YAML') if 'layout' not in frontmatter: raise ContentException('Invalid frontmatter.', 'src: {0}'.format(f.path), 'layout must be set') frontmatter.pop('url', None) parser = self._get_parser(f, frontmatter.get('parser', config.get('parser', None))) text, date = self._parse_filename(f) content = parser.parse(self._writer.from_string(bodymatter, frontmatter)) item['content'] = content item['date'] = date.strftime(self.site['date_format']) item['timestamp'] = timegm(date.utctimetuple()) if simple: item['url'] = Url.from_path(f.root.path.replace(self.src.path, ''), text) else: item['excerpt'] = re.search(r'\A.*?(?:<p>(.+?)</p>)?', content, re.M | re.S).group(1) item['tags'] = [] item['url'] = Url.from_format(config['url'], text, date, frontmatter) item.update(frontmatter) logger.debug('.. (%.3fs) %s', Timer.stop(), f.path.replace(self.src.path, '')) return item
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: {0}'.format(f.path)) try: self.config.update(Config(f.content)) except ConfigException as e: raise ConfigException(e.message, 'src: {0}'.format(f.path)) break else: logger.debug('.. no config file found')
def __init__(self, args=None): self._start = time() self.opts = self._get_opts(args) self.src = Directory(self.opts['src']) self.dest = Directory(self.opts['dest']) logger.setLevel(getattr(logging, self.opts['level'], logging.INFO)) logger.debug('>> Initializing\n.. src: {0}\n.. dest: {1}'.format( self.src, self.dest)) if self.src == self.dest: raise OptionException('Source and destination must differ.') elif self.src.path in ('/', '//') or self.dest.path in ('/', '//'): raise OptionException('Root is not a valid source or destination.') logger.debug('>> Searching for config') for ext in ('.yml', '.yaml'): f = File(normpath(self.src.path, 'config' + ext)) if f.exists: logger.debug('.. found: {0}'.format(f.path)) try: self.config.update(Config(f.content)) except ConfigException as e: raise ConfigException(e.message, 'src: {0}'.format(f.path)) break else: logger.debug('.. no config file found') for opt in ('base_url', ): if opt in self.opts: self.config[opt] = self.opts[opt] self.renderer.register({'site': self.config})
def __init__(self, post): self.path = post.path self.root = post.root self.name = post.name self.extension = post.extension logger.debug('.. {0}.{1}'.format(self.name, self.extension)) try: date, self.slug = re.match(r'(\d{4}(?:-\d{2}-\d{2}){1,2})-(.+)', self.name).groups() self.date = self._get_date(post.mtime, date) except (AttributeError, ValueError): raise PostException( 'Invalid post filename.', 'src: {0}'.format(self.path), 'must be of the format \'YYYY-MM-DD[-HH-MM]-Post-title.md\'') try: frontmatter, self.bodymatter = re.search( r'\A---\s+^(.+?)$\s+---\s*(.*)\Z', post.content, re.M | re.S).groups() except AttributeError: raise PostException('Invalid post format.', 'src: {0}'.format(self.path), 'frontmatter must not be empty') try: self.frontmatter = Config(frontmatter) except ConfigException as e: raise ConfigException('Invalid post frontmatter.', 'src: {0}'.format(self.path), e.message.lower().replace('.', '')) if 'layout' not in self.frontmatter: raise PostException('Invalid post frontmatter.', 'src: {0}'.format(self.path), 'layout must be set')
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'] = Url.join(self.config['assets_url'], '') self.config['base_url'] = Url.join( self.opts.get('base_url', self.config['base_url']), '') for setting in ('archives_url', 'posts_url', 'tags_url'): self.config[setting] = Url.join(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') containers_src = normpath(self.src.path, '_containers') for name, config in self.config['containers'].iteritems(): if op.commonprefix( (containers_src, normpath(containers_src, name))) != containers_src: raise ConfigException( 'Invalid config setting.', 'setting: containers:{0}'.format(name), 'container name contains illegal characters') try: url = Url.join(config['url']) except KeyError: raise ConfigException( 'Invalid config setting.', 'setting: containers:{0}'.format(name), 'url must be set for all containers') if re.search(r'(?:^\.{2}/|/\.{2}$|/\.{2}/)', url): raise ConfigException( 'Invalid config setting.', 'setting: containers:{0}:url'.format(name), 'path traversal is not allowed') config.update( (k, v) for k, v in self.container_defaults.iteritems() if k not in config) config['url'] = url 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')