Skip to content

Commit

Permalink
Remove Flask-FlatPages.
Browse files Browse the repository at this point in the history
Throw out all this generic stuff with a nice API,
make a some custom code with a few hard-coded paths.
  • Loading branch information
SimonSapin committed Sep 27, 2012
1 parent df78bf9 commit d1cedc6
Show file tree
Hide file tree
Showing 30 changed files with 124 additions and 122 deletions.
148 changes: 100 additions & 48 deletions exyr/__init__.py
@@ -1,51 +1,116 @@
import os
import io
import re
import math
import datetime
import itertools

import markdown
import markdown as markdown_module
import pygments.formatters
import yaml
import jinja2
from flask import Flask, render_template, send_from_directory, abort
from flaskext.flatpages import pygments_style_defs

from .public_pages import PublicPages
import werkzeug
from flask import Flask, render_template, send_from_directory, abort, url_for


app = Flask(__name__)
app.jinja_env.undefined = jinja2.StrictUndefined
app.jinja_env.globals['today'] = datetime.date.today

# The atom.xml template uses url_for(..., _external=True)
app.config['FREEZER_BASE_URL'] = 'http://exyr.org/'

pages = PublicPages(app)
app.jinja_env.globals['pages'] = pages
app.jinja_env.globals['build_date'] = datetime.date.today()


@app.template_filter(name='markdown')
def my_markdown(text):
return markdown.markdown(text, ['codehilite'] + 2 * ['downheader'])
PYGMENTS_CSS = (pygments.formatters.HtmlFormatter(style='tango')
.get_style_defs('.codehilite'))


@app.template_filter()
def markdown(text):
return markdown_module.markdown(text, ['codehilite'] + 2 * ['downheader'])


class Page(object):
root = os.path.join(app.root_path, u'pages')
suffix = '.markdown'
_cache = {}

@classmethod
def load(cls, year, name):
filename = os.path.join(cls.root, year, name) + cls.suffix
if not os.path.isfile(filename):
abort(404)
mtime = os.path.getmtime(filename)
page, old_mtime = cls._cache.get(filename, (None, None))
if not page or mtime != old_mtime:
with io.open(filename, encoding='utf8') as fd:
head = ''.join(itertools.takewhile(unicode.strip, fd))
body = fd.read()
page = cls(year, name, head, body)
cls._cache[filename] = (page, mtime)
return page

@classmethod
def years(cls):
for year in os.listdir(cls.root):
if year.isdigit():
yield year

@classmethod
def articles_by_year(cls, year):
directory = os.path.join(cls.root, year)
if not os.path.isdir(directory):
abort(404)
for name in os.listdir(directory):
if name.endswith(cls.suffix):
yield cls.load(year, name[:-len(cls.suffix)])

@classmethod
def all_articles(cls):
for year in cls.years():
for article in cls.articles_by_year(year):
yield article

def __init__(self, year, name, head, body):
self.year = year
self.name = name
self.head = head
self.body = body

@werkzeug.cached_property
def meta(self):
return yaml.safe_load(self.head) or {}

def __getitem__(self, name):
return self.meta[name]

@werkzeug.cached_property
def html(self):
return markdown(self.body)

def url(self, **kwargs):
return url_for(
'article', year=int(self.year), name=self.name, **kwargs)

app.config['FLATPAGES_HTML_RENDERER'] = my_markdown
app.config['FLATPAGES_EXTENSION'] = '.markdown'


def all_articles():
return (p for p in pages if 'published' in p.meta)

def by_date(articles):
return sorted(articles, reverse=True, key=lambda p: p['published'])


@app.route('/')
def home():
return render_template('all_posts.html', posts=by_date(all_articles()))
return render_template(
'all_posts.html', posts=by_date(Page.all_articles()))


@app.route('/about/')
def about():
return render_template('flatpage.html', page=Page.load('', 'about'))


@app.route('/tags/')
def tags():
counts = {}
for article in all_articles():
for article in Page.all_articles():
for tag in article.meta.get('tags', []):
counts[tag] = counts.get(tag, 0) + 1

Expand All @@ -59,7 +124,7 @@ def tags():
@app.route('/tags/<name>/')
def tag(name):
articles = by_date(
a for a in all_articles() if name in a.meta.get('tags', [])
a for a in Page.all_articles() if name in a.meta.get('tags', [])
)
if not articles:
abort(404)
Expand All @@ -68,22 +133,27 @@ def tag(name):

@app.route('/<int:year>/')
def archives(year):
articles = by_date(
article for article in all_articles()
if article.path.startswith(str(year) + '/')
)
if not articles:
abort(404)
articles = by_date(Page.articles_by_year(str(year)))
return render_template('archives.html', **locals())


@app.route('/<int:year>/<name>/')
def article(year, name):
return render_template('flatpage.html', page=Page.load(str(year), name))


@app.route('/<int:year>/<name>/<path:path>')
def static_in_pages(year, name, path):
return send_from_directory(Page.root, '%i/%s/%s' % (year, name, path))


@app.route('/feed.atom')
def feed():
articles = sorted(
(
# with `modified`, but defaults to `published`
(article.meta.get('modified', article['published']), article)
for article in all_articles()
for article in Page.all_articles()
),
reverse=True
)[:10]
Expand All @@ -103,7 +173,7 @@ def minify_css(css):

@app.route('/style.css')
def stylesheet():
css = render_template('style.css', pygments_style_defs=pygments_style_defs)
css = render_template('style.css', pygments_css=PYGMENTS_CSS)
css = minify_css(css)
# Add this after minification, would be removed otherwise.
css = (
Expand All @@ -115,24 +185,6 @@ def stylesheet():
return app.response_class(css, mimetype='text/css')


STATIC_EXTENSIONS = ('.jpg', '.png', '.svg', '.html', '.css', '.pdf')

# the repr() of a tuple matches the micro-syntax used by `any`
# http://werkzeug.pocoo.org/documentation/dev/routing.html#werkzeug.routing.AnyConverter
@app.route('/<path:path><any%r:type>' % (STATIC_EXTENSIONS,))
def static_in_pages(path, type):
return send_from_directory(pages.root, path + type)


@app.route('/<path:path>/')
def page(path):
return render_template('flatpage.html',
page=pages.get_or_404(path),
# sub_pages=by_date(p for p in all_articles()
# if p.path.startswith(path + '/')),
)


@app.errorhandler(404)
def not_found(e):
return render_template('404.html')
Expand Down
18 changes: 9 additions & 9 deletions exyr/freezer.py
@@ -1,8 +1,7 @@
import os
import posixpath
import mimetypes

from . import app, pages, STATIC_EXTENSIONS, all_articles
from . import app, Page
from flask.ext.frozen import Freezer, walk_directory


Expand All @@ -15,14 +14,15 @@

@freezer.register_generator
def archives():
for name in os.listdir(pages.root):
if name.isdigit():
yield {'year': name}
for year in Page.years():
yield {'year': int(year)}


@freezer.register_generator
def static_in_pages():
for filename in walk_directory(pages.root):
path, extension = posixpath.splitext(filename)
if extension in STATIC_EXTENSIONS:
yield {'path': path, 'type': extension}
for year in Page.years():
for name in os.listdir(os.path.join(Page.root, year)):
directory = os.path.join(Page.root, year, name)
if os.path.isdir(directory):
for path in walk_directory(directory):
yield {'year': int(year), 'name': name, 'path': path}
1 change: 0 additions & 1 deletion exyr/pages/2010/Flask-Static.markdown
@@ -1,5 +1,4 @@
title: "Flask-Static: yet another static website generator"
public: true
published: 2010-12-28
modified: 2011-02-21
tags:
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2010/Jinja-in-Django.markdown
@@ -1,5 +1,4 @@
title: Using Jinja2 in Django
public: true
published: 2010-12-29
tags:
- web-development
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/Frozen-Flask.markdown
@@ -1,5 +1,4 @@
title: Flask-Static is dead, long live Frozen-Flask!
public: true
published: 2011-02-21
tags: [web-development, flask]
summary: Just because the new name is so much cooler.
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/Poor-man-NTP.markdown
@@ -1,5 +1,4 @@
title: Poor man’s NTP
public: true
published: 2011-05-28
tags: [ntp]
summary: How to synchronize a Linux computer’s clock when NTP doesn’t work.
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/event-loop.markdown
@@ -1,7 +1,6 @@
title: Single-threaded event loop for file input and timers
published: 2011-02-27
tags: [snippets]
public: true
summary: |
To wait for various events and without polling, a blocking threads for each
event is the obvious solution. However, multi-threading comes with its
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/gedit-plugins-packaged.markdown
@@ -1,5 +1,4 @@
title: Two Gedit 3 plugins packaged for Arch Linux
public: true
published: 2011-07-03
tags: [gedit, packaging, archlinux]
summary:
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/git-mirrors.markdown
@@ -1,7 +1,6 @@
title: Mirroring a Gitorious repository to GitHub
published: 2011-04-30
tags: [git, backups]
public: yes
summary: |
You can not make Gitorious or GitHub push to somewhere else, so mirroring
them requires a few more steps.
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/gnome-terminal-tabs.markdown
@@ -1,6 +1,5 @@
title: Programatically open multiple GNOME Terminal tabs
published: 2011-02-20
public: yes
tags: [snippets]
summary: |
To run several tasks in parallel and keep their output separated, run each
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/hashing-passwords.markdown
@@ -1,5 +1,4 @@
title: Hashing passwords the Right Way
public: true
published: 2011-12-05
tags: [passwords, web-development, snippets]
summary:
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/inotify-run.markdown
@@ -1,5 +1,4 @@
title: Run a script on file changes with inotify
public: true
published: 2011-10-13
tags: [inotify, snippets]
summary:
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/multicorn.markdown
@@ -1,5 +1,4 @@
title: "Multicorn: Access foreign data in PostgreSQL with Python"
public: true
published: 2011-11-07
tags: [kozea]
summary:
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/random-pronounceable-passwords.markdown
@@ -1,6 +1,5 @@
title: Random pronounceable passwords
published: 2011-02-11
public: yes
tags: [passwords, snippets]
summary: |
Using a sample text for statistics and Markov chains, we can generate
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/virtualenv-HOWTO.markdown
@@ -1,5 +1,4 @@
title: virtualenv HOWTO
public: true
published: 2011-06-10
tags: [packaging]
summary: Slides for an introduction to virtualenv and pip I gave recently.
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2011/weasyprint.markdown
@@ -1,5 +1,4 @@
title: WeasyPrint renders HTML+CSS to PDF
public: true
published: 2011-11-02
tags: [kozea, weasyprint]
summary:
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2012/csswg-invited-expert.markdown
@@ -1,7 +1,6 @@
title: Invited Expert at W3C’s CSSWG
published: 2012-09-17
tags: [weasyprint, W3C]
public: true
summary: |
I just joined [W3C’s *CSS Working Group*](http://www.w3.org/Style/CSS/)
as an *Invited Expert*.
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2012/flask-weasyprint.markdown
@@ -1,7 +1,6 @@
title: Flask-WeasyPrint
published: 2012-07-19
tags: [kozea, weasyprint]
public: true
summary: |
I just released [Flask-WeasyPrint](http://packages.python.org/Flask-WeasyPrint/)

Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2012/fosdem.markdown
@@ -1,7 +1,6 @@
title: I’m going to FOSDEM
published: 2012-02-01
tags: [fosdem, kozea]
public: true
summary: |
I’m going to [FOSDEM](http://fosdem.org/2012/) this week-end.
See you there!
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2012/tinycss-css-parser.markdown
@@ -1,7 +1,6 @@
title: "tinycss: a new CSS parser for Python"
published: 2012-04-05
tags: [kozea, weasyprint]
public: true
summary: |
I made [tinycss](http://packages.python.org/tinycss/), a new free software
CSS parser for Python. It is extensible, well-documented, well-tested
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2012/weasyprint-at-pyconfr.markdown
@@ -1,7 +1,6 @@
title: WeasyPrint at PyConFR 2012
published: 2012-09-10
tags: [kozea, weasyprint]
public: true
summary: |
I’ll be [giving a talk](http://www.pycon.fr/2012/schedule/presentation/16/)
about WeasyPrint at PyConFR on Sunday.
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/2012/weasyprint-is-bsd.markdown
@@ -1,7 +1,6 @@
title: WeasyPrint is now BSD-licensed
published: 2012-03-22
tags: [kozea, weasyprint]
public: true
summary: |
We just changed [WeasyPrint](http://weasyprint.org/)’s license from
AGPL to BSD.
Expand Down
1 change: 0 additions & 1 deletion exyr/pages/about.markdown
@@ -1,5 +1,4 @@
title: About
public: true

Hi, I’m Simon Sapin and this is my personal website. I’m a software engineer at
[Kozea](http://kozea.fr), a member of [W3C’s CSS Working Group](
Expand Down

0 comments on commit d1cedc6

Please sign in to comment.