このBlogは移転しました。今後は aish.dev を御覧ください。

GitLab PagesのDeployが失敗する場合の対処方法

GitLab Pages にドキュメント生成するたびに同じ落とし穴にハマるので、ここに記しておく。

GitLab Pagesにドキュメントを生成すると、ビルドが成功しても、その後のDeployが失敗する場合がある。

この問題は次のチケットでGitLabに報告済みで、もうすでにCloseされているが、まだ発生するようだ。

gitlab.com

回避方法は以下の通り。

  • artifacts: に指定するドキュメントの path: は、かならず public という名前のディレクトリとする。
  • public ディレクトリは、かならず新規に作成する。

Sphinxをつかってpdfやドキュメントを生成するときは、次のように public ディレクトリを新規作成し、生成したファイルを public ディレクトリに移動するようにする。

.gitlab-ci.yml の例を以下に示す。

# Sphinxによるドキュメント生成の例

image: atsuoishimoto/ubuntu-latex

pages:
  script:
    - make latexpdf
    - make html
    - mkdir public
    - mv _build/latex/*.pdf public/
    - mv _build/html/* public/
  artifacts:
    paths:
    - public
  only:
    - master

ダイクストラおばちゃん

最近、偶然プログラミング初心者に接する機会が続いた。初心者にもいろいろあるが、中でも印象深い女性のことを思い出したので書いておきたい。

大昔、ちょっとした業務改善のシステムを開発することになって、実際にその業務を行っている事務や経理の方々に話を伺ったことがある。

この時お会いした年配の女性が、すさまじいほどのExcelのエキスパートだった。当時のPC環境はまだまだ原始的で、動作も不安定だったが、彼女は独学でExcelマクロを開発し、かなりの業務の自動化に成功していた。

話を聞いてみると、とくにプログラミングの勉強をしたことはなく、本を数冊読んだ程度で、あとはExcelのヘルプだけを頼りにマクロを組み上げたらしい。まだインターネットもさほど普及していない時代だ。ほぼ自分の頭だけで考えて、ここまでたどり着いたのだろう。素晴らしい出来栄えだった。

一番驚いたのは、彼女が重要なソフトウェア工学の原則を独力で再発見していたことだ。「処理は機能ごとの関数に分割するとわかりやすいんですよ」とか、「意味のある名前を付けてあげるのが一番大事なんです」とか、自分の発見を体系化し、明確なルールとして運用していたのだ。私は戦慄した。なんてことだ、このおばちゃんダイクストラやんけ!

この時点で、彼女がExcelを使い始めてからまだ数年。どこまで成長するのだろうか。私は背筋に冷たいものを感じながら彼女に礼を言い、インタビューを終了したのだった。

API star - Python3用 Web API framework

最近何度か名前を目にした Webアプリケーションフレームワーク API Star を試してみた

github.com

まだ開発中のフレームワークだが、Pythonの型アノテーションをうまく利用して、Web APIを簡単に開発できるようになっている。

インストール

githugからソースをダウンロードして実行してみた

$ git clone https://github.com/encode/apistar.git
$ pip install -e .

アプリケーションの作成

apistar コマンドでひな形を作成する。

$ apistar new test
test/app.py
test/tests.py

アプリケーションの実行

作成した app.py を実行する。

$ python app.py start
 * Running on http://127.0.0.1:8080/ (Press CTRL+C to quit)
 * Restarting with fsevents reloader
 * Debugger is active!
 * Debugger PIN: 233-378-852

APIの追加

app.py を次のように修正する

from apistar import Include, Route
from apistar.frameworks.wsgi import WSGIApp as App
from apistar.handlers import docs_urls, static_urls
from apistar import typesystem

def welcome(name=None):
    if name is None:
        return {'message': 'Welcome to API Star!'}
    return {'message': 'Welcome to API Star, %s!' % name}


# ここから追加
class IntProp(typesystem.Integer):
    minimum = 1
    maximum = 5

class EnumProp(typesystem.Enum):
    enum = ['one', 'two', 'three']

class RetType(typesystem.Object):
    properties = {
        'strprop': typesystem.string(max_length=100),
        'intprop': IntProp,
        'enumprop': EnumProp,
    }

def api1(intprop: IntProp, enumprop: EnumProp) -> RetType:
    return RetType(strprop='abc', intprop=intprop, enumprop=enumprop)

# ここまで追加


routes = [
    Route('/', 'GET', welcome),
    Route('/api1', 'GET', api1),   # この行を追加
    Include('/docs', docs_urls),
    Include('/static', static_urls)
]

app = App(routes=routes)


if __name__ == '__main__':
    app.main()

この例では、2つのデータ型を定義して、関数 api1() の引数として指定している。一つは 1 から 5 までの整数値を表す型で、もう一つは文字列のone, two, three のいずれかの文字列の列挙型だ。

app.py を再起動し、ブラウザで http://127.0.0.1:8080/docs/# を開く。

f:id:atsuoishimoto:20170912083047p:plain

作成した APIが表示され、INTERACT をクリックするとAPIを実行できる。

f:id:atsuoishimoto:20170912083326p:plain

Webページの作成

APIだけではなく、Jinjaテンプレートを使って通常のHTMLも作成できる。

def generate_htmlpage(name, templates: Templates):
    templ = templates.get_template('hello.html')    # templates/hello.htmlを使用
    return templ.render(username=name)

RDBの利用

RDBへのアクセス手段として、SQLAlchemyとDjango-ORMを使用できる。

from apistar import Include, Route
from apistar.frameworks.wsgi import WSGIApp as App
from apistar.handlers import docs_urls, static_urls

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String

from apistar.backends import sqlalchemy_backend

Base = declarative_base()

# Testテーブルの定義
class Test(Base):
    __tablename__ = "Test"
    id = Column(Integer, primary_key=True)
    name = Column(String)


def create_test(session: sqlalchemy_backend.Session, name: str):
    # Testテーブルにデータを登録し、idを返す
    test = Test(name=name)
    session.add(test)
    session.flush()
    return {'id': test.id}

routes = [
    Route('/create_test', 'GET', create_test),
    Include('/docs', docs_urls),
    Include('/static', static_urls)
]

# Configure database settings.
settings = {
    "DATABASE": {
        "URL": "sqlite:///test.sqlite",
        "METADATA": Base.metadata
    }
}

app = App(
    routes=routes,
    settings=settings,
    commands=sqlalchemy_backend.commands,  # Install custom commands.
    components=sqlalchemy_backend.components  # Install custom components.
)


if __name__ == '__main__':
    app.main()

この例では、SQLAlchemyとSQLiteを使ってデータを登録している。app.pycreate_tables コマンドでテーブルを作成できる。

$ python app.py create_tables

tse 0.0.15リリース

tse 0.0.15をリリースした。

  • 組み込み関数 P() を追加

Python3以降では、print(...) の代わりに P(...) と書けるようにした。

  • コマンド置換

Python3以降では、`command` で コマンドとして command を実行するようにした。

例:

$ tse -b 'P(`ps`)'
  PID TTY          TIME CMD
18447 pts/1    00:00:00 bash
18487 pts/1    00:00:00 tse
18488 pts/1    00:00:00 sh
18489 pts/1    00:00:00 ps

これだけだとあまり役に立たないが、Python3.6以降では、fプリフィックスをつけて変数を展開できる。

$ ls | tse -s '\.py$' 'P(f`wc {L}`.rstrip())'
       0       0       0 __init__.py
     436    1191   14696 main.py

静的サイトジェネレータ Miyadaiku

ここ数年、www.python.jp は、 Pelican を使って構築していた。

Pelican は実績のある静的サイトジェネレータで使いやすくはあるが、基本的にはBlogサイトの構築ツールであり、あまり柔軟性や拡張性には重点を置かれていないように感じていた。www.python.jp 以外でもいくつかのサイト構築に使用したが、以下のような不満を感じていた。

  • アーティクルに Jinjaテンプレートを書きたい

    reStructuredTextやMarkdown には、定型文などを記述するため手段として、エクステンションやディレクティブなどを開発して組み込む仕組みがあるが、開発・管理はそれなりに面倒で、そう気軽には作れない。Jinjaのマクロ機能などを使って、手軽に拡張できる仕組みがほしい。

  • アーティクル全体を検索するAPIがない。このため、Blogサイトなどでよくある、サイドバーに「最近の記事ボックス」などを表示する機能の実装が難しい。

  • サイトのデザインテーマを指定して既存のデザインを共有する機能があるが、テーマとして指定できるのは一つだけしかなく、指定したテーマ以外からテンプレートやCSSJavascriptの共有手段する手段がない。

  • 目次の作成機能がない

  • 画像ファイルなどを、専用のディレクトリに保存しなければならない。アーティクルと同じディレクトリに保存したい。

などだ。

それでは、ということで独自に静的サイトジェネレータを開発した。

miyadaiku.github.io

Jinja2テンプレート

Miyadaikuでは、reStructuredTextとMarkdown用にJinja2を記述するための 拡張 を提供しており、テンプレートだけでなく、コンテンツ中でもJinja2でHTMLを生成できる。

Sample Miyadaiku article
---------------------------

This is a *plain* reST article.

.. jinja::
    {% for i in range(10) %}
      <p>{{ i }}</p>
    {% endfor %}

テーマ

作成したテンプレートやCSS、画像などの素材・コンテンツからPythonのパッケージを作成し、テーマとして再利用できる。Miyadaikuには組み込みのテーマとして

などがある。

また、別パッケージとして、次のようなテーマを提供している。

テーマは通常のPythonパッケージと同様に pipなどでインストールし、同時に複数のテーマを利用できる。また、テーマが利用しているテーマは自動的にインポートされる。

API

Jinja2テンプレートからコンテンツの表示や検索を行うAPIにアクセスできるので、独自のページ構成や目次などを自由に作成できる。

コンテンツオブジェクトはアーティクルなどのコンテンツや画像などの素材ファイルなどにアクセスするオブジェクトで、タイトルやカテゴリなどのメタデータの参照や、ページへのリンク作成などを行える。

コンテンツコレクション は、すべてのコンテンツを管理するオブジェクトで、条件を指定してコンテンツを検索できる。検索条件として、プロパティやタグなどのメタデータや、格納ディレクトリなどを指定できる。

tse 0.0.14リリース

tse 0.0.14をリリースした。

  • 組み込み関数 E() を追加

tseからコマンドを使うとき、毎度毎度 subprocess.check_output('ls', shell=True, universal_newline=True) とするのは面倒なので、E('ls') で呼び出せるようにした。

例:

$ tse -b 'for l in E("ls").splitlines():print(l)'
HISTORY
LICENSE
MANIFEST.in
README.rst