Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Remove Flask-FlatPages.

Throw out all this generic stuff with a nice API,
make a some custom code with a few hard-coded paths.
  • Loading branch information...
commit d1cedc633c1df4d1f395441a56d01cfef8dcebd6 1 parent df78bf9
@SimonSapin authored
Showing with 124 additions and 122 deletions.
  1. +100 −48 exyr/__init__.py
  2. +9 −9 exyr/freezer.py
  3. +0 −1  exyr/pages/2010/Flask-Static.markdown
  4. +0 −1  exyr/pages/2010/Jinja-in-Django.markdown
  5. +0 −1  exyr/pages/2011/Frozen-Flask.markdown
  6. +0 −1  exyr/pages/2011/Poor-man-NTP.markdown
  7. +0 −1  exyr/pages/2011/event-loop.markdown
  8. +0 −1  exyr/pages/2011/gedit-plugins-packaged.markdown
  9. +0 −1  exyr/pages/2011/git-mirrors.markdown
  10. +0 −1  exyr/pages/2011/gnome-terminal-tabs.markdown
  11. +0 −1  exyr/pages/2011/hashing-passwords.markdown
  12. +0 −1  exyr/pages/2011/inotify-run.markdown
  13. +0 −1  exyr/pages/2011/multicorn.markdown
  14. +0 −1  exyr/pages/2011/random-pronounceable-passwords.markdown
  15. +0 −1  exyr/pages/2011/virtualenv-HOWTO.markdown
  16. +0 −1  exyr/pages/2011/weasyprint.markdown
  17. +0 −1  exyr/pages/2012/csswg-invited-expert.markdown
  18. +0 −1  exyr/pages/2012/flask-weasyprint.markdown
  19. +0 −1  exyr/pages/2012/fosdem.markdown
  20. +0 −1  exyr/pages/2012/tinycss-css-parser.markdown
  21. +0 −1  exyr/pages/2012/weasyprint-at-pyconfr.markdown
  22. +0 −1  exyr/pages/2012/weasyprint-is-bsd.markdown
  23. +0 −1  exyr/pages/about.markdown
  24. +0 −27 exyr/public_pages.py
  25. +5 −6 exyr/templates/atom.xml
  26. +2 −2 exyr/templates/flatpage.html
  27. +3 −3 exyr/templates/layout.html
  28. +1 −1  exyr/templates/style.css
  29. +2 −4 exyr/templates/utils.html
  30. +2 −1  setup.py
View
148 exyr/__init__.py
@@ -1,37 +1,96 @@
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'])
@@ -39,13 +98,19 @@ def by_date(articles):
@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
@@ -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)
@@ -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]
@@ -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 = (
@@ -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')
View
18 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
@@ -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}
View
1  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:
View
1  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
View
1  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.
View
1  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.
View
1  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
View
1  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:
View
1  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.
View
1  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
View
1  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:
View
1  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:
View
1  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:
View
1  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
View
1  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.
View
1  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:
View
1  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*.
View
1  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/)
View
1  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!
View
1  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
View
1  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.
View
1  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.
View
1  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](
View
27 exyr/public_pages.py
@@ -1,27 +0,0 @@
-from werkzeug import cached_property
-from flaskext.flatpages import FlatPages
-
-
-class PublicPages(FlatPages):
- """
- In debug mode, prefix the title of pages that do not have a true-ish
- `public` metadata with 'DRAFT: '. Otherwise hide these pages.
- """
-
- @cached_property
- def _pages(self):
- # cached_property(f).func is f
- pages = FlatPages._pages.func(self)
- if self.app.debug:
- return pages
- else:
- return dict((path, p) for path, p in pages.iteritems() if p.public)
-
- def _parse(self, *args, **kwargs):
- page = super(PublicPages, self)._parse(*args, **kwargs)
- page.public = page.meta.get('public', False)
- if not page.public:
- # Pages are required to have a title
- page.meta['title'] = 'DRAFT: ' + page.meta['title']
- return page
-
View
11 exyr/templates/atom.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>exyr.org</title>
<id>{{ url_for('home', _external=True) }}</id>
@@ -10,24 +10,23 @@
<name>Simon Sapin</name>
</author>
<rights type="html">
- &lt;p&gt;© Copyright {{ build_date.year }} by Simon Sapin.&lt;/p&gt;
+ &lt;p&gt;© Copyright {{ today().year }} by Simon Sapin.&lt;/p&gt;
&lt;p&gt;Content licensed under a Creative Commons
Attribution-NonCommercial-ShareAlike License.&lt;/p&gt;
</rights>
{% for updated, article in articles %}
- <entry>
- <id>{{ url_for('page', path=article.path, _external=True) }}</id>
+ <entry>{% set url = article.url(_external=True) %}
+ <id>{{ url }}</id>
<title>{{ article.title }}</title>
<updated>{{ updated.strftime('%Y-%m-%dT%H:%M:%SZ') }}</updated>
<published>{{ article.published.strftime('%Y-%m-%dT%H:%M:%SZ') }}</published>
<author>
<name>Simon Sapin</name>
</author>
- <link href="{{ url_for('page', path=article.path, _external=True) }}" />
+ <link href="{{ url }}" />
<content type="html">
{{ article.html }}
</content>
</entry>
{% endfor %}
</feed>
-
View
4 exyr/templates/flatpage.html
@@ -15,7 +15,7 @@
{%- endfor %}
</p>
</header>
- {{ page }}
+ {{ page.html|safe }}
</article>
{% else %}
<header>
@@ -23,7 +23,7 @@
</header>
{% if page.html %}
<section>
- {{ page }}
+ {{ page.html|safe }}
</section>
{% endif %}
{% endif %}
View
6 exyr/templates/layout.html
@@ -41,7 +41,7 @@
<ul>
<li><a href="{{ url_for('home') }}">Posts</a></li>
<li><a href="{{ url_for('tags') }}">Tags</a></li>
- <li><a href="{{ url_for('page', path='about') }}">About</a></li>
+ <li><a href="{{ url_for('about') }}">About</a></li>
<li><a href="{{ url_for('feed') }}">Subscribe <img alt=""
src="{{ url_for('static', filename='feed-icon.png') }}"></a></li>
</ul>
@@ -54,8 +54,8 @@
<footer>
{#- See <rights/> in atom.xml too #}
- <p>© Copyright {{ build_date.year }} by
- <a href="{{ url_for('page', path='about') }}">Simon Sapin</a>.</p>
+ <p>© Copyright {{ today().year }} by
+ <a href="{{ url_for('about') }}">Simon Sapin</a>.</p>
<p>Content licensed under <a rel=license
href="http://creativecommons.org/licenses/by-nc-sa/3.0/">
Creative Commons BY-NC-SA</a>.</p>
View
2  exyr/templates/style.css
@@ -164,7 +164,7 @@ hr:before {
margin: 0 .5em;
}
-{{ pygments_style_defs('tango') }}
+{{ pygments_css }}
.codehilite, blockquote {
background-color: #eee;
View
6 exyr/templates/utils.html
@@ -9,15 +9,13 @@
<section class=article_list>
{% for page in articles %}
<article>
- <h3><a href="{{ url_for('page', path=page.path) }}">
+ <h3><a href="{{ page.url() }}">
{{ page.title }}</a></h3>
<p>{{ html5_date(page.published) }}</p>
{{ page.summary|markdown|safe }}
- <p><a href="{{ url_for('page', path=page.path) }}">Read more…</a></p>
+ <p><a href="{{ page.url() }}">Read more…</a></p>
</article>
{% endfor %}
</section>
{% endif %}
{% endmacro %}
-
-
View
3  setup.py
@@ -11,9 +11,10 @@
license="BSD License",
install_requires=[
'Flask',
- 'Flask-FlatPages',
'Frozen-Flask>=0.9',
'Flask-Script',
'Pygments',
+ 'PyYAML',
+ 'Markdown',
],
)
Please sign in to comment.
Something went wrong with that request. Please try again.