Skip to content
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ ve/
*.py[co]
*.egg
*.egg-info
*.komodo*
7 changes: 6 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@

packages = find_packages('src'),
package_dir = {'': 'src'},
package_data={'pyll': ['templates/default.html'],},
package_data={
'pyll': ['templates/default.html',
'quickstart.tar.gz'],
},
zip_safe = False,

test_suite = 'nose.collector',
Expand All @@ -30,6 +33,8 @@
'markdown',
'docutils',
'Jinja2',
'python-dateutil==1.5',
'PyYAML',
],
)

31 changes: 15 additions & 16 deletions src/pyll/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from datetime import datetime
import imp
import logging
import ConfigParser
import yaml
from optparse import OptionParser
from os import makedirs, getcwd, getlogin
from os.path import splitext, join, dirname, split, abspath, getctime,\
Expand Down Expand Up @@ -126,8 +126,8 @@ def _parse(self, input_data):

# parse the page
parser_cls = parser.get_parser_for_filename(page['path'])
with open(path, 'r', encoding='utf8') as f:
parser_inst = parser_cls(self.settings, f.read())
with open(path, 'r', encoding='utf-8') as f:
parser_inst = parser_cls(self.settings, f.read(), page['path'])

try:
parsed = parser_inst.parse()
Expand Down Expand Up @@ -257,23 +257,23 @@ def run(self):
def quickstart(settings):
login = getlogin()

config = ConfigParser.SafeConfigParser()
config.add_section('pyll')
author_name = raw_input("Author Name [%s]: " % login) or login
config.set('pyll', 'author_name', author_name)
author_email_default = '%s@example.org' % login
author_email = raw_input("Author Email [%s]: " % author_email_default) or author_email_default
config.set('pyll', 'author_email', author_email)
website_url_default = 'http://www.example.org'
website_url = raw_input("Website URL [%s]: " % website_url_default) or website_url_default
config.set('pyll', 'website_url', website_url)
config = {'pyll': {
'author_name': author_name,
'author_email': author_email,
'website_url': website_url,
}}

# before writing the settings file, make sure the _lib dir exists
if not exists(settings['lib_dir']):
makedirs(settings['lib_dir'])

with open(settings['settings_path'], 'wb') as configfile:
config.write(configfile)
with open(settings['settings_path'], 'wb', encoding='utf-8') as configfile:
configfile.write(yaml.dump(config, default_flow_style=False))

# extract template
tmpl_path = join(dirname(abspath(__file__)), 'quickstart.tar.gz')
Expand All @@ -282,7 +282,7 @@ def quickstart(settings):
tar.extractall(path=settings['project_dir'])
tar.close()

return dict(config.items('pyll'))
return config['pyll']

def main():
parser = OptionParser(version="%prog " + __version__)
Expand All @@ -306,8 +306,7 @@ def main():
'lib_dir': join(project_dir, '_lib'),
'url_path': join(project_dir, '_lib', 'urls.py'),
'settings_path': join(project_dir, '_lib', 'settings.cfg'),
'build_time': datetime.today(),
'date_format': '%Y-%m-%d'}
'build_time': datetime.today()}

# configure logging
logging_level = LOGGING_LEVELS.get(options.logging, logging.INFO)
Expand All @@ -322,9 +321,9 @@ def main():

# read settings file
if exists(settings['settings_path']):
config = ConfigParser.SafeConfigParser()
config.read(settings['settings_path'])
settings.update(dict(config.items('pyll')))
with open(settings['settings_path'], 'rb', encoding='utf-8') as configfile:
config = yaml.safe_load(configfile.read())
settings.update(config['pyll'])
logging.debug('settings %s', settings)

# initialize site
Expand Down
92 changes: 27 additions & 65 deletions src/pyll/parser.py
Original file line number Diff line number Diff line change
@@ -1,85 +1,30 @@
import datetime
import re
import logging
import yaml
from os.path import splitext


class ParserException(Exception):
"""Exception raised for errors during the parsing."""
pass

class Parser(object):
output_ext = None

def __init__(self, settings, source):
def __init__(self, settings, source, filename):
self.settings = settings
self.source = source
self.headers = {}
self.text = ''
self.filename = filename

def _parse_headers(self):
"""
Parses and removes the headers from the source.
"""
META_RE = re.compile(
r'^[ ]{0,3}(?P<key>[A-Za-z0-9_-]+):\s*(?P<value>.*)')
lines = self.source.splitlines()
for num, line in enumerate(lines):
match = META_RE.match(line)
if match:
key = match.group('key').strip().lower()
value = match.group('value').strip()
if value:
# custom header transformation
header_method = getattr(self, '_parse_%s_header' % key,
None)
if header_method:
value = header_method(value)
self.headers[key] = value
num_last_match = num
else:
break
# remove header lines from input source
try:
del lines[:num_last_match + 1]
except UnboundLocalError:
pass

# check if a blank line followed the header lines and remove it
try:
if not lines[0]:
del lines[0]
except IndexError:
pass
self.text = '\n'.join(lines)

def _parse_tags_header(self, value):
"""
Parses the value from the 'tags' header into a list.
"""
return [t.strip() for t in value.split(',')]

def _parse_date_header(self, value):
"""
Parses the date header into a python datetime.datetime object.
The value must be in the format as specified by the 'date_format'
setting; otherwise a ParserException will be thrown.
"""
format = self.settings['date_format']
try:
return datetime.datetime.strptime(value, format)
except ValueError as error:
raise ParserException(error)

def _parse_updated_header(self, value):
return self._parse_date_header(value)

def _parse_status_header(self, value):
"""
Checks that the value of the 'status' header is 'live', 'hidden' or
'draft'. If not 'live' is returned.
Parses the headers from the source.
"""
if value in ('live', 'hidden', 'draft'):
return value
return 'live'
self.headers = yaml.safe_load(self.header_raw) if self.header_raw != '' else {}
if not 'status' in self.headers \
or self.headers['status'] not in ('live', 'hidden', 'draft'):
self.headers['status'] = 'live'

def _parse_text(self):
"""
Expand All @@ -88,7 +33,24 @@ def _parse_text(self):
"""
return self.text

def _split_input(self):
"""
Segregates header from actual text.
Used to later parse these parts a different way.
"""
parts = self.source.split("\n\n", 1)
if len(parts) < 2:
parts = self.source.split("---\n", 1)
if len(parts) >= 2:
self.header_raw = parts[0]
self.text = parts[-1]
else:
logging.warn("'%s' has no headers, only content - at least 'title' will be missing.", self.filename)
self.header_raw = ''
self.text = self.source

def parse(self):
self._split_input()
self._parse_headers()
self._parse_text()
return (self.headers, self.text)
Expand Down
Binary file modified src/pyll/quickstart.tar.gz
Binary file not shown.