Projekt-Vorlagen mit cookiecutter    Veröffentlicht:


cookiecutter ("Ausstechförmchen") erzeugt aus Vorlagen beliebige Projektgrundstrukturen und kann dabei vordefinierte Werte einsetzen sowie fehlende erfragen.

Ziel von cookiecutter und anderen sogl. Scaffolding-Werkzeugen ist, beim Erstellen neuer Projekte nur noch die nötigsten Schritte per Hand durchführen zu müssen.

Existiert eine passende Vorlage, kann mit

# cookiecutter <Name der Vorlage>

im aktuellen Verzeichnis ein Projekt angelegt werden. Die benötigten Vorlagen können dabei lokal und auch auf GitHub liegen. Ein Beispiel aus der Projektdokumentation:

# cookiecutter https://github.com/audreyr/cookiecutter-pypackage.git

Die von GitHub bezogene Vorlage wird zusätzlich im Verzeichnis ~/.cookiecutters/ abgelegt und kann dann später wie eine lokale Vorlage direkt verwendet werden:

# cookiecutter ~/.cookiecutter/cookiecutter-pypackage

Angaben, die unabhängig von den Vorlagen immer gleich bleiben, wie bspw. der Name des Entwicklers oder die E-Mail-Adresse, lassen sich in der Datei ~/.cookiecutterrc speichern:

default_context:
  full_name: "fuller"
  email: "fuller@daemogorgon.net"
  github_username: "fuller"
cookiecutters_dir: "/home/.../my-custom-cookiecutters-dir/"

Im Abschnitt default_context können beliebige Schlüssel-Wert-Paare stehen. Sollen die Vorlagen woanders als im Standardverzeichnis liegen, definiert man dies mit cookiecutters_dir.

Eigene Vorlagen

Grundlegende Struktur

Als Basis für eine eigene Vorlage kann entweder eine vorhandene Vorlage mit passender Ausrichtung dienen oder man entwickelt eine eigene Vorlage auf Basis folgende Struktur:

meine-vorlage/
├── {{ cookiecutter.repo_name }}/
│   └── ...
└── cookiecutter.json

Verzeichnis- und Dateinamen und Dateiinhalte der Form {{ cookiecutter.<Parameter> }} werden durch cookiecutter ersetzt. Die Datei cookiecutter.json enthält Angaben zu Parametern und voreingestellten Werten:

{
  "full_name": "John Doe",
  "email": "jdoe@mail.com",
  "github_username": "johnd",
  "project_name": "My New Project",
  "repo_name": "my_project",
  "project_short_description": "My project ...",
  "release_date": "2013-12-11",
  "year": "2013",
  "version": "0.1"
}

Parameter

Die Parameter sind Vorlage-spezifisch, Werte können aber in ~/.cookiecutterrc bereits vorbelegt werden:

full_name:
Name des Entwicklers
email:
E-Mail-Adresse
github_username:
Login für GitHub
project_name:
offizieller Projektname
repo_name:
Name des Reositories
project_short_description:
Kurzbeschreibung des Projekts
release_date:
Releasedatum
year:
Jahr
version:
Version

Die Template-Sprache Jinja2

Mit Jinja2 können Vorlagen verschiedenster Art und Kompliziertheit erstellt werden. Dank seiner Python-ähnlichen Sprache ist die Erstellung einfach und schnell möglich.

Struktur

Jinja nutzt zwei grundlegende Strukturen. Mit

{% for item in list %}
  ...
{% endfor %}

werden Kontrollstrukturen abgebildet wie Schleifen oder Konditionalbedingungen. Mit

{{ item.href }}

werden Variablen verarbeitet, d.h. ihr Wert wird bei der Verarbeitung der Vorlage eingesetzt.

Variablen

Auf Attribute von Variablen kann auf zweierlei Art zugegriffen werden:

  • item.href
  • item['href']

Variablen erhalten Werte mit set:

{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
{% set key, value = call_something() %}

Whitespace-Kontrolle

Normalerweise werden durch den Ersetzungsprozess an die Stelle von Statements wie {% endblock %} Leerzeilen gesetzt. Das führt normalerweise zu einem unerwünscht "luftigen" Ergebnis. Seit Version 2.2 werden abschließende Newlines automatisch entfernt.

Fügt man ein Minus-Zeichen an den Anfangs- oder Endblock, so werden Whitespaces vor und nach dem Block entfernt:

{% for item in seq -%}
  {{ item }}
{%- endfor %}

Filter

Neben den üblichen Kontrollstrukturen besitzt Jinja einen umfangreichen Fundus an sog. Filtern. Diese können mit Hilfe des Pipe-Symbols | verkettet werden. Ein Beispiel:

{{ name|striptags|title }}}

entfernt aus dem Wert der Variable name alle HTML-Tags und konvertiert die Wortanfänge in Grobuchstaben.

Kommentare

Kommentare werden mit # erstellt:

{# ...
  ...
#}

Escaping

Natürlich können auch Jinja-Vorlagen mit Hilfe von Jinja erstellt werden. Dazu ist allerdings ein Escaping der geschweiften Klammern Notwendig:

{{ '{{' }}

Template-Vererbung

Jinja ermöglicht die Entwicklung eines Basis-Templates, dass von anderen Templates geerbt werden kann. Im Basis-Template werden sog. block s definiert, die von den Erben ersetzt werden.

Basis-Template base.html:

...
{% block head %}
...
{% endblock %}

Erbendes Template:

{% extends "base.html" %}
...
{% block head %}
  {{ super() }}
  ...
{% endblock %}
...

Im Basis-Template wird hier ein allgemeines Kopfelement definiert, dass dann vom Kind verwendet wird. Mittels super() wird dabei der ursprüngliche Block übernommen und anschließend ergänzt.

Soll ein schon definierter Block weiter unten im Template erneut zum Einsatz kommen, lässt sich mit self bewerkstelligen:

<title>{%  block title %}{% endblock %}</title>
<h1>{{  self.title() }}</h1>
{%  block body %}{% endblock %}

Blöcke können beliebig veschachtelt werden; zur Verbesserung der Lesbarkeit erlaubt Jinja die Nennung des Blocknamens im endblock-Element. Allerdings sind "äußere" Variablen in einem geschachtelten Block per se nicht verfügbar, es sei denn, sie sind explizit dafür vorbereitet:

{% for item in seq %}
  <li>{% block loop_item %}{{ item }}{% endblock %}</li>
{% endfor %}

Hier ist item im Block loop_item nicht bekannt, da ein Kind-Template diesen Block überschreiben könnte. Mit scope (ab Version 2.2) ist item trotzdem verfügbar:

{% for item in seq %}
  <li>{% block loop_item scoped %}{{ item }}{% endblock %}</li>
{% endfor %}

HTML-Escaping

Wenn ein HTML-Template erstellt werden soll, besteht das Risiko, dass Variablen Werte wie < oder & enthalten und damit syntaktisch relevant sind für den Code. Jinja bietet dazu den Filter e an:

{{ user.username|e }}

Makros

Makros sind vergleichbar mit selbst definierten Funktionen.

{% macro input(name, value='', type='text', size=20) -%}
  <input type="{{ type }}" name="{{ name }}" value="{{
      value|e }}" size="{{ size }}">
{%- endmacro %}

Aufrufen lässt sich ein Makro so:

<p>{{ input('username') }}</p>
<p>{{ input('password', type='password') }}</p>

Befindet sich die Makrodefinition in einer anderen Templatedatei, muss diese vorher importiert werden.

Testen eines Templates

Jinja2 bietet nur eine API an, kein Werkzeug, um ein Template zu testen. Die API ist glücklicherweise so einfach, dass man schnell ein solches Werkzeug zusammenstellen kann.

Sämtliche Vorlagen müssen hierbei im Verzeichnis templates liegen.

# generate.py

from ast import literal_eval
import sys
from jinja2 import Environment, FileSystemLoader

template_dir = "templates"
jinja = Environment(loader=FileSystemLoader(template_dir))


def render(template_filename, args):
  tpl = jinja.get_template(template_filename)
  content = tpl.render(args)

  return content


def help():
    print("Usage: {0} <template filename> <output|-> <dict>".format(sys.argv[0]), file=sys.stderr)
    print("e.g. /usr/bin/python3 generate.py tmp.tmp - '{\"a\":1, \"bla\": \"blub\"}'", file=sys.stderr)
    print("You *must* use the single quotes, otherwise your shell will try to interpret some characters!", file=sys.stderr)

if __name__ == "__main__":
    if len(sys.argv) != 4:
        help()
        sys.exit(1)

    template_filename = sys.argv[1]
    output = sys.argv[2]

    args = sys.argv[3]
    kwargs = literal_eval(args)

    if template_filename == "":
        print("You must give a template filename as first argument", file=sys.stderr)
        sys.exit(1)

    rendered_content = render(template_filename, args)

    if output == "-":
        output_handle = sys.stdout
        output_handle.write(content)
    else:
        with open(output, 'w') as output_handle:
            output_handle.write(content)

Beispiele

Der Autor von cookiecutter hat auf GitHub eine Beispiel-Vorlage für Python-Projekte veröffentlicht. Auf der cookiecutter-Projektseite befindet sich eine Liste mit Vorlagen für verschiedenste Zwecke.

Nikola aufsetzen    Veröffentlicht:


Nikola ist ein Werkzeug zur Erzeugung statischer Webseiten.

Warum statisch und nicht dynamisch wie WordPress, Joomla etc.? Es gibt einige Gründe, die für statische Seiten sprechen:

Sicherheit:
Bis auf den Webserver ist auf Serverseite (neben dem Betriebssystem natürlich) keine weitere Software involviert, wenn statische Inhalte ausgeliefert werden. Bei klassischen LAMP-Stacks wie MediaWiki, WordPress, Joomla und Drupal läuft neben einer Datenbank (oft MySQL oder jetzt auch MariaDB) immer eine Skriptsprache. Gerade PHP befindet sich oft im Fokus von Angreifern (s. bspw. die PHP-Themenseite auf Heise).
Performanz und Ressourcen:
Selbst kleine und einfache Webserver wie thttpd (bzw. der Fork sthttpd) können statische Seiten sehr schnell ausliefern, wie ein (etwas älterer) Benchmark des thttpd-Entwicklers zeigt. Der Ressourcenverbrauch hält sich dabei in Grenzen und das Einrichten des Webservers reduziert sich im Wesentlichen auf die Angabe des Verzeichnisses, in dem sich die HTML-Dateien befinden.
Vendor Lock-In:
Die Verwendung von Datenbanken, die Abhängigkeit von speziellen Eingabeformaten und die enge Anbindung an eine bestimmte Webserver-Software erschweren einen Wechsel des Systems, wenn einer der Bausteine nicht mehr gepflegt wird oder massive Sicherheitsprobleme aufweist. Bei statischen Seiten besteht dieses Problem nicht, denn jeder Webserver kann statische Seiten ausliefern.
Verwaltung und Backup:
Die Quelldateien für einen Generator wie Nikola bzw. die fertigen HTML-Dateien lassen sich einfach wegsichern, entweder direkt mit einer Backup-Software oder besser mit Hilfe einer Versionsverwaltung wie Mercurial oder Subversion. Zwar lassen sich auch Datenbanken recht einfach dumpen, aber für eine vollständige Sicherung muss zusätzlich die Anbindung der Datenbank und die Integration der Skriptsprache bzw. die zugehörige Konfiguration des Webservers gesichert werden.

Es gibt auch Gründe, die gegen statische Seiten sprechen:

Installation zusätzlicher Software:
CMS-Systeme benötigen für die Verwaltung nur den Webbrowser. Um statische Seiten zu erstellen, muss mindestens ein Editor installiert sein. Meist wird ein spezieller Editor wie bspw. Bluefish verwendet. Bluefish ist ein HTML-Editor mit Syntax Highlighting, integrierter Projektverwaltung und Deploymentfunktionalität.
Design der Website:
CMS-Systeme garantieren ein einheitliches Aussehen der Website über einen Template-Mechanismus. Die meisten HTML-Editoren bieten einen solchen Mechanismus auch an, ggf. muss aber für die Erstellung der Templates eine weitere Auszeichnungssprache beherrscht werden.
Erstellen/Bearbeiten von Seiten:
CMS-Systeme bieten in der Regel eine WYSISYG-Bearbeitung der Inhalte an, wie man sie von Office-Produkte wie LibreOffice kennt. Die meisten HTML-Editoren und Generatoren hingegen erfordern Kenntnisse von Auszeichnungssprachen wie HTML, CSS, Markdown, reST, Jinja, HTML, CSS usw.
Aufwand:
Der Aufwand bei der Erstellung/Änderung einer Seite ist höher als bei klassischen CMS-Lösungen: Quelldatei erstellen/bearbieten, kompilieren, auf den Webserver laden.

Für Entwickler, die den Umgang mit Auszeichnungssprachen, Compilern und Versionsverwaltungen gewohnt sind, ist das alles kein Hindernis. Allerdings ist HTML als Auszeichnungssprache doch recht wortreich und die Umsetzung eines konsistenten Layouts ist ohne Unterstützung durch Werkzeuge nur bedingt umzusetzen.

Hier kommen die Generatoren ins Spiel. Sie bieten alternativ zu HTML Support für vereinfachte Auszeichnungssprachen wie Markdown und reST und nutzen Templates, um für ein einheitliches Look-and-Feel zu sorgen.

Es gibt viele statische Blog-/Website-Generatoren (jekyll, hyde, pelican, blacksmith, acrylamid u.v.m.), warum gerade Nikola?

  • wird aktiv entwickelt
  • Übersetzungen in mehr als 10 Sprachen
  • Syntax-Highlighting von Quellcode
  • flexible Eingabe: reST, Markdown, bbcode, Wiki, HTML, txt2tags, textile
  • umfangreich dokumentiert

Installation

Nikola benötigt entweder Python 2 ab der Version 2.6 oder Version 3.3 und neuer. Da das 3.3er Release (noch) nicht in allen Distributionen verfügbar ist, installiert man sich seine eigene Version. Notwendige Abhängigkeiten sind (hier für Debian-basierte Distributionen):

  • libbz2dev
  • libreadlinedev
  • libncurses5-dev
  • libgdbm-dev
  • liblzma-dev
  • libsqlite3-dev
  • libxml2-dev
  • libxslt1-dev
  • libssl-dev
  • libz-dev

Da es sich um eine Serveranwendung handelt, wird Tkinter nicht benötigt.

Die Installation folgt dem üblichen Dreischritt configure, make, make install. Ziel der Installation ist hier das Verzeichnis $HOME/opt/python:

configure --prefix=$HOME/opt/python
make
make install

Damit die Shell den frisch installierten Python-Interpreter auch findet, müssen die Umgebungsvariablen PATH und LD_LIBARY_PATH. Dies kann bspw. in $HOME/.profile erfolgen:

PATH=$HOME/opt/python/bin:$PATH
LD_LIBARY_PATH=$HOME/opt/python/lib;$LD_LIBARY_PATH

Damit die Änderungen aktiv werden, muss eine neue Shell gestartet werden. Im nächsten Schritt wird eine "virtuelle" Umgebung für Nikola eingerichtet. Ab Version 3.3 bietet Python selbst ein Modul dazu an; für ältere Versionen kann man virtualenv verwenden:

cd $HOME/opt
python3.3 -m venv nikola
cd nikola
source bin/activate

Die Umgebung ist jetzt aktiv (zu erkennen an (nikola)-Prompt), es fehlen allerdings das distribute-Paket und pip:

wget http://python-distribute.org/distribute_setup.py
bin/python distribute_setup.py install
bin/easy_install pip

Jetzt ist alles für die eigentliche Installation des Generators vorbereitet:

bin/pip install -U nikola

Hilfreich ist auch das requests-Modul, wenn man zusätzliche Themes installieren möchte:

pip install requests

Die sehr nützliche automatische Rebuild-Funktion wird vom livereload-Modul bereit gestellt. Die derzeit in PyPI vorliegende Fassung hat allerdings noch einen Fehler, der in den Github-Quellen schon beseitigt wurde. Daher installiert man das Modul direkt von Github:

pip install http://github.com/lepture/python-livereload/zipball/master

Website einrichten

Mit dem "aktivierten" Nikola kann nun der Quellcode für eine neue Website erstellen werden. Zunächst erstellt man die Grundstruktur:

nikola init daemogorgon.net
cd daemogorgon.net

Die quasi noch leere Seite baut man mit dem Befehl nikola build. Entweder öffnet man die nach dem Bau im Unterordner output liegen HTML-Dateien manuell oder nutzt den in Nikola eingebauten Webserver. Dieser wird mit dem Befehl nikola serve gestartet und stellt die Dateien unter http://localhost:8000 bereit.

Erstellte Dateien werden mit nikola clean entfernt. Kommt es beim Bauen zu einem dbm-Fehler (22: Invalid argument), kann das Löschen der Datei .doit.db helfen.

In der Datei conf.py können verschiedene Aspekte des Auftritts angepasst werden. Zu den wichtigen Variablen gehören:

  • BLOG_AUTHOR
  • BLOG_TITLE
  • SITE_URL
  • BLOG_EMAIL
  • BLOG_DESCRIPTION
  • DEFAULT_LANG

Themes installieren

Sollte einem das Standard-Theme nicht zusagen, kann man ein alternatives Theme installieren. nikola install_theme -l listet die derzeit verfügbaren Themes auf. Mit nikola install_theme monospace installiert man bspw. das monospace-Theme. Um das neue Theme zu nutzen, wird in der Datei conf.py der Wert der Variable THEME angepasst: THEME = "monospace".

Je nach Länge des Blog-Titels kann es notwendig sein, dass man die Fontgröße anpassen muss. Die betreffende Datei liegt unter themes/monospace/assets/css/themes.css, der Identifier heißt #blog_title. Ein passender Wert ist 2em.

Wenn Disqus als Kommentarsystem nicht benutzt werden soll, muss man die zugehörigen Zeilen in den Template-Dateien themes/monospace/templates/index.tmpl und themes/monospace/templates/post.tmpl entfernen bzw. auskommentieren:

##        % if not post.meta('nocomments'):
##            ${comments.comment_link(post.permalink(), post.base_path)}
##        % endif

Eigene Themes lassen sich relativ einfach auf der Basis eines vorhandenen Themes erstellen.

Inhalte einstellen

Posts

Neue Posts erzeugt man mit dem Befehl nikola new_post. Nach Eingabe eines Titels erhält man den Pfad zur gerade erzugten Datei, dann noch mit Inhalt gefüllt werden muss. Das Grundgerüst sieht folgendermaßen aus:

.. title: [gerade eingegeben]
.. slug: [entspricht dem Seitennamen; "bereinigte" Version des Titels, also
     entfernte Leerzeichen etc.]
.. date: [aktuelles Datum]
.. tags:
.. link: [Hinweis auf Originalquelle?]
.. description: [hilfreich für Suchmaschinenoptimierung]

Dann folgt der Inhalt der Seite.

Oft wird ein Teaser formuliert, der auch im RSS-Feed angezeigt wird. Der Teaser wird durch einen speziellen Kommentar abgegrenzt:

.. TEASER_END

Seiten

Seiten sind vergleichbar zu Posts, außer:

  • dass sie nicht auf der Frontseite erscheinen
  • nicht im RSS-Feed auftauchen
  • das story.tmpl nutzen

Die Erzeugung entspricht auch der Erzeugung eines Posts, nur dass die Option -p beigefügt wird: nikola new_post -p.

Dateien ausliefern

Dateien werden im Ordner files abgelegt und können dann verlinkt oder über eine Umleitung (s. Linkerhaltend arbeiten).

Linkerhaltend arbeiten

Beim Umbau einer Website kann es sein, dass Link-erhaltend gearbeitet werden muss. Besteht Zugriff auf die Konfiguration des Webservers, kann dort ein Redirect eingerichtet werden. Ist dies nicht möglich, bietet Nikola mit REDIRECTIONS einen eigenen Redirect-Mechanismus an. In conf.py muss dazu für jede Umleitung ein Paar ("von", "nach") eingetragen werden:

REDIRECTIONS = [ ("von", "nach") ]

Ein Beispiel: Der alte Auftritt (eine WordPress-Instanz) stellte unter wp-content/uploads/wmanager_021.tgz eine Datei zum Download bereit. Der Nikola-basierte Auftritt stellt Dateien im Ordner files bereit:

REDIRECTIONS = [
  ("wp-content/uploads/wmanager_021.tgz", "files/wmanager_021.tgz")
  ]

Verweise auf andere Posts/Seiten

Mit Hilfe der reST-Direktive :doc: kann auf andere Seiten eines Auftritts verwiesen werden:

Verweis auf die Seite: :doc:`nikola-static-site-and-blog-generator`

Arbeiten an Posts/Seiten

Um nicht immer manuell mit nikola build die Generierung starten zu müssen, kann auch mit nikola auto gearbeitet werden. Der in Nikola eingebettete Webserver stellt dann nicht nur unter der Adresse http://localhost:8000 die Website bereit, sondern überwacht die Quelldateien. Werden Änderungen registriert, startet Nikola automatisch eine Generierung und aktualisiert das Browserfenster.

Deployment

Nikola bietet mit dem Befehl nikola deploy eine Möglichkeit, die in der Variablen DEPLOY_COMMANDS (zu finden in der Datei conf.py) hinterlegten Zeilen auszuführen. Ein Beispiel:

rsync -av --delete  output/*  user@host.net:/path/to/dir

Optimal wäre hier eine SSH-Authentifizierung mittels Public Key-Verfahren. Besteht keine Möglichkeit, eine passwortlose Authentifizierung umzusetzen, können die Dateien immer noch manuell übertragen werden. Aus mir nicht bekannten Gründen versucht Nikola, die Dateien mit Hilfe des vorgefertigten Disqus-Account nikolademo zu verteilen. Um das zu verhindern und den in DEPLOY_COMMANDS hinterlegten Befehl ausführen zu können, müssen in conf.py zwei Variablen angepasst werden:

COMMENT_SYSTEM = ""
COMMENT_SYSTEM_ID = ""

Erst jetzt läuft das Deployment und Nikola bzw. rsync/ssh fragt nach einem Passwort.

Inhalte © 2014 - fuller - Impressum - Haftungsausschluss - Datenschutz

Creative Commons Lizenzvertrag
daemogorgon.net von fuller ist lizenziert unter einer Creative Commons Namensnennung - Weitergabe unter gleichen Bedingungen 3.0 Unported Lizenz.