GitLab PagesのDeployが失敗する場合の対処方法
GitLab Pages にドキュメント生成するたびに同じ落とし穴にハマるので、ここに記しておく。
GitLab Pagesにドキュメントを生成すると、ビルドが成功しても、その後のDeployが失敗する場合がある。
この問題は次のチケットでGitLabに報告済みで、もうすでにCloseされているが、まだ発生するようだ。
回避方法は以下の通り。
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 を試してみた
まだ開発中のフレームワークだが、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/#
を開く。
作成した APIが表示され、INTERACT
をクリックするとAPIを実行できる。
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.py
の create_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サイトなどでよくある、サイドバーに「最近の記事ボックス」などを表示する機能の実装が難しい。
サイトのデザインテーマを指定して既存のデザインを共有する機能があるが、テーマとして指定できるのは一つだけしかなく、指定したテーマ以外からテンプレートやCSS、Javascriptの共有手段する手段がない。
目次の作成機能がない
などだ。
それでは、ということで独自に静的サイトジェネレータを開発した。
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には組み込みのテーマとして
miyadaiku.themes.base - 必要最低限のHTMLテンプレートと、Google Analytics や Open Graph などの、汎用的なマクロを提供
miyadaiku.themes.pygments - Pygmentsによる構文ハイライト用CSS
などがある。
また、別パッケージとして、次のようなテーマを提供している。
- miyadaiku.themes.jquery - プロジェクトに jQueryを組み込む
- miyadaiku.themes.bootstrap4 - プロジェクトにBootstrap4を組み込む
- miyadaiku.themes.fontawesome - プロジェクトにFontawesomeを組み込む
テーマは通常の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