naritoブログ

このブログはDjangoとBootstrap4で作成されました
ソースコード

通常のPythonライブラリ・フレームワークを配布する

プログラミング関連 Python pytest tox coveralls 約10日前
2017年8月7日17:55
前記事
Djangoアプリを配布する〜⑥おまけ
https://torina.top/detail/387/

Djangoアプリを配布する〜シリーズはDjnago特有の事情があったので、それを除いた通常のPythonライブラリ・フレームワークの配布方法についてです。

今回はサンプルとして、
https://github.com/naritotakizawa/getsize
を基に解説していきます。

このリポジトリは、
Pythonでディレクトリのサイズを取得する https://torina.top/detail/316/
をコマンドラインツールとして使えるようにしたものです。

ファイル・ディレクトリの構成は以下のようになっています。
getsize/
    __init__.py
    main.py
tests/
    __init__.py
    test_main.py
.gitignore
.travis.yml
LICENSE
MANIFEST.in
README.rst
setup.py
tox.ini



getsizeディレクトリのmain.pyに処理が書かれています。
__init__.pyは、全て空欄です。

tests/test_main.py


今回ですが、「pytest」というテストフレームワークを利用しています。(pip install pytest)
pytestで書くとこんな感じになります。
import os
import pytest
from getsize import main


def test_approximate_size():
    with pytest.raises(ValueError) as excinfo:
        main.approximate_size(-1)
    assert str(excinfo.value) == 'number must be non-negative'

    assert main.approximate_size(10000) == '9.8 KiB'

    assert main.approximate_size(
        10000, a_kilobyte_is_1024_bytes=False
    ) == '10.0 KB'

    with pytest.raises(ValueError) as excinfo:
        main.approximate_size(100**100)
    assert str(excinfo.value) == 'number too large'


def test_get_size():
    sample_file = os.path.join('tests', 'sample', 'sample1.png')
    assert main.get_size(sample_file) == 473831

    sample_dir = os.path.join('tests', 'sample')
    assert main.get_size(sample_dir) == 473831*4



pytestでは、以下のようにassert文を使ったシンプルなテストが行なえます。
エラーレポートも詳細に表示されわかりやすく、pytestではなくunittestで書いたテストも実行してくれる便利なやつです。
assert main.approximate_size(10000) == '9.8 KiB'



LICENSE


今回はMITライセンスです。
MIT License

Copyright (c) 2017 Narito Takizawa

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



README.rst


リードミーです。
=======
getsize
=======

.. image:: https://travis-ci.org/naritotakizawa/getsize.svg?branch=master
    :target: https://travis-ci.org/naritotakizawa/getsize

.. image:: https://coveralls.io/repos/github/naritotakizawa/getsize/badge.svg?branch=master
    :target: https://coveralls.io/github/naritotakizawa/getsize?branch=master


ファイル・ディレクトリのサイズを柔軟に取得するコマンドラインツール

Requirement
===========
:Python: 3.4以上
 
 
Install
=======
::

    pip install -U https://github.com/naritotakizawa/getsize/archive/master.tar.gz


Usage
=====
::

    usage: pysize [-h] [-p PATH] [-s SORT] [-r] [-f] [-u]
    
    optional arguments:
      -h, --help            show this help message and exit
      -p PATH, --path PATH  file or dir path
      -s SORT, --sort SORT  size or name
      -r, --reverse         show reverse
      -f, --full            show full path
      -u, --unit            unit scale

    # カレントディレクトリ内のファイル・ディレクトリのサイズを表示する
    pysize
    
    # target_path内のファイル・ディレクトリを表示する 
    pysize -p target_path
    
    # 10MB のような人に優しい形で表示する
    pysize -p target_path -u
    
    # 相対パスではなくフルパスで表示する
    pysize -p target_path -f
    
    # サイズ順に表示する("size" か "name")
    pysize -s size -u
    
    # 昇順・降順を逆にする
    # pysize -s size -r -u



Djangoシリーズでも触れましたが、この2つはTravis CIとCoverallsのバッジです。
当然ですが、それぞれのサービスと連携していなければちゃんと表示されません。
.. image:: https://travis-ci.org/naritotakizawa/getsize.svg?branch=master
    :target: https://travis-ci.org/naritotakizawa/getsize

.. image:: https://coveralls.io/repos/github/naritotakizawa/getsize/badge.svg?branch=master
    :target: https://coveralls.io/github/naritotakizawa/getsize?branch=master



setup.py


次はsetup.pyです。setup.py自体の説明はDjangoシリーズの②でしたので、変更点だけ説明していきます。
import os
import sys
from setuptools import find_packages, setup
from setuptools.command.test import test as TestCommand


class PyTest(TestCommand):
    user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]

    def initialize_options(self):
        TestCommand.initialize_options(self)
        self.pytest_args = []

    def finalize_options(self):
        TestCommand.finalize_options(self)
        self.test_args = []
        self.test_suite = True

    def run_tests(self):
        #import here, cause outside the eggs aren't loaded
        import pytest
        errno = pytest.main(self.pytest_args)
        sys.exit(errno)

with open(os.path.join(os.path.dirname(__file__), 'README.rst'), 'rb') as readme:
    README = readme.read()

 
setup(
    name='getsize',
    version='0.1',
    packages=find_packages(exclude=('tests',)),
    include_package_data=True,
    license='MIT License',
    description='Command Line Tools For Get File and Directory Size',
    long_description=README.decode('utf-8'),
    url='https://github.com/naritotakizawa/getsize',
    author='Narito Takizawa',
    author_email='toritoritorina@gmail.com',
    classifiers=[
        "Environment :: Console",
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Python',
        "Programming Language :: Python :: 3",
        'Programming Language :: Python :: 3.4',
        'Programming Language :: Python :: 3.5',
        'Programming Language :: Python :: 3.6',
        "Topic :: Software Development :: Libraries :: Python Modules",
    ],
    tests_require = ['tox'],
    cmdclass = {'test': PyTest},
    entry_points={'console_scripts': [
        'pysize = getsize.main:main',
    ]},
)


Djangoシリーズとの大きな違いは、setup.py testでpytestが行われるようになったことです。
以下の部分がsetup.py test で動くようにしている部分です。
from setuptools.command.test import test as TestCommand


class PyTest(TestCommand):
    user_options = [('pytest-args=', 'a', "Arguments to pass to py.test")]

    def initialize_options(self):
        TestCommand.initialize_options(self)
        self.pytest_args = []

    def finalize_options(self):
        TestCommand.finalize_options(self)
        self.test_args = []
        self.test_suite = True

    def run_tests(self):
        #import here, cause outside the eggs aren't loaded
        import pytest
        errno = pytest.main(self.pytest_args)
        sys.exit(errno)
...
...
...
    tests_require = ['pytest', 'pytest-cov'],
    cmdclass = {'test': PyTest},


pysize というコマンドで、getsizeパッケージのmainモジュールのmain関数を呼ぶようにしています。
    entry_points={'console_scripts': [
        'pysize = getsize.main:main',
    ]},



MANIFEST.in


include LICENSE
include README.rst



.gitignore


Githubに上げる必要のないファイルを書いていきます。
プロジェクトに変更があったら都度 git status として、不要なファイルが追加されてないかを簡単に確認するようにもしときましょう。
*.pyc
*.pyo
.eggs
*.egg-info
.cache
__pycache__

.coverage
.tox

/build/
/dist/



tox.ini


toxの設定です。
[tox]
envlist = flake8,py34,py35,py36

[testenv]
commands = python -Wall setup.py test -a "--cov getsize --cov-report term-missing"

[testenv:flake8]
basepython = python3.6
commands = flake8 getsize
deps = flake8



Djangoシリーズの⑥との大きな違いは、pytest用のコマンドになったことです。
-cov getsizeはカバレッジの取得をgetsizeディレクトリに、--cov-report term-missingはテストが通っていない行を表示するオプションです。
今回はsetup.pyのtests_requireにて['pytest', 'pytest-cov']としているので、tox内でこれらをインストールする必要はありません。
commands = python -Wall setup.py test -a "--cov getsize --cov-report term-missing"


.travis.yml


他のライブラリに依存していないので、記述もシンプルです。
今回もカバレッジ取得CIサービスのCoverallsを利用しています。
language: python

before_script:
  - pip install coveralls
 
python:
    - "3.4"
    - "3.5"
    - "3.6"

script:
    - python -Wall setup.py test -a "--cov getsize --cov-report term-missing"

after_success:
    - coveralls