( ゚∀゚)o彡゚ PyPI! PyPI!

先週土曜日に Python mini hack-a-thon に参加してきました。

当初は Sphinx Extension を作ろうかなーと思っていましたが、その前の火曜日に参加した Expert Python Programming 読書会で「Python の動的型チェックライブラリ作って使ってました」なんて言って PyPI に上げる宣言しつつもやっていなかったので、それをやることに。

PyPI って何よ?

PyPI とは、 Python Package Index の略で、色々な人が作った様々な Python のモジュールを集めている場所です。
Rubygems, PerlCPAN, PHPPEAR, HaskellCabal, OCamlHump に当たるものです。
なんとなくパッケージリポジトリとそのコマンドがごっちゃになっている気がしますが、要はそういうものです。

モジュールを PyPI に登録する

まずは宣言の通りに typechecker を登録します。

モジュールを登録する際は このあたり が参考になります。

アカウントを登録する

PyPI にモジュールを登録する際は、 PyPI のアカウントが必要です。

登録ページ で必要事項を記入して登録します。

登録すると、登録時に入力したメールアドレスに activate 用の URL が送られてくるので、アクティベートして登録完了です。

setup.py を書く

モジュールを登録するには、 setup.py を書かなければいけないのですが、 buildout を使って開発をしているような普通の開発者であればおそらく開発対象のモジュール用に setup.py を書いていると思います。

とはいえ、書くのは必要最低限のバージョンやモジュール名くらいの以下のようなものでしょう。

#-*- coding:utf-8 -*-

import setuptools

version = '0.1.0'

setuptools.setup(
    name='typechecker',
    packages=['typechecker'],
    install_requires=[
        ],
    version=version)

これに加えて開発者情報やモジュールの種類などのメタデータを追記します。

#-*- coding:utf-8 -*-

import setuptools
import typechecker

version = '0.1.0'

setuptools.setup(
    name='typechecker',
    version=typechecker.__version__,
    packages=['typechecker'],
    install_requires=[
        ],
    author=typechecker.__author__,
    author_email='shoma.h4a+pypi@gmail.com',
    license=typechecker.__license__,
    url='https://github.com/shomah4a/typechecker',
    description='This module provides dynamic type check mechanism.',
    long_description=typechecker.__doc__,
    classifiers='''
Programming Language :: Python
Development Status :: 2 - Pre-Alpha
License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Programming Language :: Python :: 2
Programming Language :: Python :: 2.6
Programming Language :: Python :: 2.7
Topic :: Software Development :: Libraries :: Python Modules
Topic :: Utilities
'''.strip().splitlines())

ちょいちょい長くなりました。

classifiers は このあたり から適当に拾ってきて設定すればいいようです。

long_description はモジュールの docstring をそのまま持ってきました。

'''
This module provides dynamic type check mechanism.

Normal Type Check:

   >>> from typechecker import typeinfo, list_, tuple_, has_attrs, list_, tuple_
   >>> from typechecker import Callable, and_
   >>> @typeinfo(int, x=int, y=int)
   ... def add(x, y):
   ...     return x + y
   >>> add(10, 20)
   30
   >>> add('aa', 'bb')
   Traceback (most recent call last):
     File "<console>", line 1, in <module>
     File "typechecker/typeinfo.py", line 113, in call
       _check_arg(f, assigned, types)
     File "typechecker/typeinfo.py", line 84, in _check_arg
       f.__name__))
   TypeError: In function add, argument "y" required "int". "str" found.

Tuple Type Check:

   >>> @typeinfo(int, val=tuple_(int, str))
   ... def test(val):
   ...     return len(val)
   ...
   >>> test((19, 'aaa'))
   2
   >>> test('aaa')
   Traceback (most recent call last):
     File "<console>", line 1, in <module>
     File "typechecker/typeinfo.py", line 113, in call
       _check_arg(f, assigned, types)
     File "typechecker/typeinfo.py", line 84, in _check_arg
       f.__name__))
   TypeError: In function test, argument "val" required "tuple[int, str]". "str" found.

List Type Check:

    >>> @typeinfo(int, val=list_(int))
    ... def test(val):
    ...     return 10
    ...
    >>> test([1,2,3])
    10
    >>> test([1,2,'aaa'])
    Traceback (most recent call last):
      File "typechecker/typeinfo.py", line 113, in call
        _check_arg(f, assigned, types)
      File "typechecker/typeinfo.py", line 84, in _check_arg
        f.__name__))
    TypeError: In function test, argument "val" required "list[int]". "list" found.

Complex Type Check:
 
    >>> @typeinfo(int, val=tuple_(list_(int)))
    ... def test(val):
    ...     return 10
    ...
    >>> test(([1],))
    10
    >>> test((['aa'],))
    Traceback (most recent call last):
      File "/usr/local/python2.7/lib/python2.7/doctest.py", line 1254, in __run
        compileflags, 1) in test.globs
      File "<doctest typechecker[12]>", line 1, in <module>
        test((['aa'],))
      File "typechecker/typeinfo.py", line 113, in call
        _check_arg(f, assigned, types)
      File "typechecker/typeinfo.py", line 84, in _check_arg
        f.__name__))
    TypeError: In function test, argument "val" required "tuple[list[int]]". "tuple" found.


Structural subtyping:

    >>> @typeinfo(int, f=Callable)
    ... def test(f):
    ...     return 10
    ...
    >>> test(map)
    10
    >>> test(1)
    Traceback (most recent call last):
      File "<console>", line 1, in <module>
      File "typechecker/typeinfo.py", line 113, in call
        _check_arg(f, assigned, types)
      File "typechecker/typeinfo.py", line 84, in _check_arg
        f.__name__))
    TypeError: In function test, argument "f" required "hasattr[__call__]". "int" found.


And check:

    >>> @typeinfo(int, f=and_(Callable, ContextManager))
    ... def test(f):
    ...     return 10
    ...
    >>> test(file)
    10
    >>> test(map)
    Traceback (most recent call last):
      File "<console>", line 1, in <module>
      File "typechecker/typeinfo.py", line 113, in call
        _check_arg(f, assigned, types)
      File "typechecker/typeinfo.py", line 84, in _check_arg
        f.__name__))
    TypeError: In function test, argument "f" required "And[hasattr[__call__], hasattr[__enter__, __exit__]]". "builtin_function_or_method" found.
'''

doctest そのまんまって感じです。

パッケージを登録

で、 setup.py を書き終えたらパッケージを登録します。

$ python setup.py register
... (略) ...
We need to know who you are, so please choose either:
 1. use your existing login,
 2. register as a new user,
 3. have the server generate a new password for you (and email it to you), or
 4. quit
Your selection [default 1]: 
1
Username: *************
Password: 
Registering typechecker to http://pypi.python.org/pypi

register コマンドを発行すると、アカウントをどうするか聞かれるので、 1 を選んでユーザ名とパスワードを入力します。

これでパッケージ情報が PyPI に登録されます。

登録が済んだら次はパッケージ本体をアップロードします。

$ python setup.py sdist upload

ここでも同様にアカウントを聞かれるので入力します。

ここで間違えなければパッケージが PyPI にアップロードされるはずです。

これでパッケージの登録は終了です。結構あっさりと登録されるものですね。

まとめ

「ねんがんの PyPI にデビューしたぞ!」

で、 hack-a-thon 中にモジュールの docstring を書いたりしながら

の二つのモジュールをアップロードしました。

ついでに調子にのって

の二つもアップロードしたりしました。

え、 sphinx extension? とりあえず hello, sphinx な directive は作りましたよもちろん。