from setuptools import setup, find_packages
setup(
name='sample',
version='1.0.0',
url='https://github.com/pypa/sampleproject',
packages=find_packages(exclude=['tests*']),
)
# パッケージ登録
$ python setup.py register
# 指示に従って入力
# パッケージアップロード
$ python setup.py sdist upload
終わりです。簡単ですね。
例えば「sample」というパッケージを妄想する
sample
├── MANIFEST.in
├── README.rst
├── sample <- Pythonパッケージ
│ └── __init__.py
├── setup.cfg
├── setup.py
└── tests <- テストコード
├── __init__.py
└── test_sample.py
setup(
name='sample',
version='1.0.0',
description='A sample Python project',
long_description=long_description,
url='https://github.com/pypa/sampleproject',
author='The Python Packaging Authority',
author_email='pypa-dev@googlegroups.com',
license='MIT',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'Topic :: Software Development :: Build Tools',
'License :: OSI Approved :: MIT License',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 3',
],
keywords='sample setuptools development',
packages=find_packages(exclude=['contrib', 'docs', 'tests*']),
install_requires=['peppercorn'],
)
参考: https://github.com/pypa/sampleproject/blob/master/setup.py
Framework :: Django
Framework :: Django :: 1.4
Framework :: Django :: 1.5
Framework :: Django :: 1.6
Framework :: Django :: 1.7
Framework :: Django :: 1.8
Development Status :: 1 - Planning
Development Status :: 2 - Pre-Alpha
Development Status :: 3 - Alpha
Development Status :: 4 - Beta
Development Status :: 5 - Production/Stable
Development Status :: 6 - Mature
Development Status :: 7 - Inactive
1.2.0.dev1 # Development release
1.2.0a1 # Alpha Release
1.2.0b1 # Beta Release
1.2.0rc1 # Release Candidate
1.2.0 # Final Release
1.2.0.post1 # Post Release
15.10 # Date based release
23 # Serial release
バージョンナンバーは、メジャー.マイナー.パッチとし、バージョンを上げるには、
- APIの変更に互換性のない場合はメジャーバージョンを、
- 後方互換性があり機能性を追加した場合はマイナーバージョンを、
- 後方互換性を伴うバグ修正をした場合はパッチバージョンを上げます。
プレリリースやビルドナンバーなどのラベルに関しては、メジャー.マイナー.パッチの
形式を拡張する形で利用することができます。
def find_version(*file_paths):
version_file = read(*file_paths)
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
version_file, re.M)
if version_match:
return version_match.group(1)
raise RuntimeError("Unable to find version string.")
setup(
...
version=find_version("package", "__init__.py")
...
)
with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
long_description = f.read()
setup(
...
long_description=long_description,
...
)
via https://github.com/pypa/sampleproject/blob/master/setup.py
from setuptools import find_packages
setup(
...
packages=find_packages(exclude=['tests*'])
...
)
# × 除外しない
packages=find_packages()
# ○ 除外する
packages=find_packages(exclude=['tests*'])
# 除外しなかった場合
import sample
import tests.test_sample # トップレベルでテストコードがインストールされてしまう
setup(
...
install_requires=[
'Hoge',
]
...
)
$ pip freeze > requirements.txt
# インストール済みパッケージの一覧を出してくれる
# バージョンを固定
Spam==1.0.0
Ham==2.1.0
Egg==1.3.0
with open('requiments.txt') as fp:
requires = fp.readlines()
install_requires=requires
$ pip install Spam
$ pip freeze | grep Spam
Spam==1.1.0
# => Spamの最新版 1.1.0 が インストール
$ pip install sample
$ pip freeze | grep Spam
Spam==1.0.0
# => install_requires の バージョン固定により1.0.0になってしまう
_人人人人人人人人人人人人人人人_
> 突然のダウングレード!!! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄
# 別パッケージが同様に、別のバージョン番号を固定で指定してると、
# バージョン競合が発生しインストールに失敗するケースも...
# そもそもバージョンを指定しない
install_requires=['Ham']
# 最低限必要なバージョンだけを記載
install_requires=['Ham>=1.0.0']
# 範囲を限定的にするとか
install_requires=['Ham>=1.0.0,<2']
# requirements.txtにもバージョン指定なし、範囲指定を記述可能
# install_requiresにバージョンを固定しないというのが大事
2/3の互換性を保つライブラリを利用する(pipでインストール可能)
__future__ で Py3 の機能を利用する
from __future__ import division, print_function, absolute_import, unicode_literals
2to3 というツールはメンテコストが高そうなのでオススメしないです
import six
d = {'hoge1': 'fuga', 'hoge2': 'fuga', }
# python2
for k, v in d.iteritems():
print(k, v)
# python3
for k, v in d.items():
print(k, v)
# python2, 3 両方
for k, v in six.iteritems(d):
print(k, v)
$ pip install -e .
# そのまま動作確認可能
$ python
Python 3.4.3 (default, Mar 23 2015, 04:19:36)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sample
>>> sample.main()
Call your main application code here
$ pip install tox
[tox]
envlist=py27,py34,py35,pypy
[testenv]
commands=
python setup.py test
# 全バージョンでテストを実行
$ tox
# バージョンを指定してテスト実行
$ tox -e py35
$ py.test test_assert.py
...
=========== FAILURES =============
__________ test_main _____________
def test_main():
lst1 = ['test1', 'test2']
lst2 = ['test1', 'test3']
> assert lst1 == lst2, 'Not equal lists'
E AssertionError: Not equal lists
E assert ['test1', 'test2'] == ['test1', 'test3']
E At index 1 diff: 'test2' != 'test3'
E Full diff:
E - ['test1', 'test2']
E ? ^
E + ['test1', 'test3']
E ?
import sys
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)
setup(
...
tests_require = ['pytest'],
cmdclass = {'test': PyTest},
...
)
# py.testが実行される
$ python setup.py test
$ python setup.py test -a "--cov sample --cov-report term-missing"
include README.rst
include tox.ini
include setup.cfg
recursive-include docs *
recursive-include tests *
include sample/*.dat
# ビルド
$ python setup.py sdist
# 直下のdistにsdistが生成されます
$ ls dist
sample-0.0.1.tar.gz
$ pip install wheel
$ python setup.py bdist_wheel
# python3にしか対応しない
$ python setup.py bdist_wheel --python-tag=py3
[wheel]
universal = 1
$ python setup bdist_wheel
$ ls
sample-0.0.1-py2.py3-none-any.whl
$ ./setup.py register
running register
running egg_info
writing tesdat.egg-info/PKG-INFO
writing top-level names to sample.egg-info/top_level.txt
writing dependency_links to sample.egg-info/dependency_links.txt
reading manifest file 'sample.egg-info/SOURCES.txt'
writing manifest file 'sample.egg-info/SOURCES.txt'
running check
We need to know who you are, so please choose either:
1. use your existing login,
...
Registering sample to https://pypi.python.org/pypi
Server response (200): OK
I can store your PyPI login so future submissions will be faster.
(the login will be stored in /home/tell-k/.pypirc)
Save your login (y/N)?y
[distutils]
index-servers =
pypi
[pypi]
username: tell-k
password: xxxxxxxxxxxx
# 事前に「setup.py」 の記述内容をチェック
$ python setup.py check -r -s
$ python setup.py sdist bdist_wheel upload
# × uplaod単体では利用できない
$ python upload
running upload
error: No dist file created in earlier command
$ pip install twine
$ twine uplaod dist/*
[distutils]
index-servers=
pypi
pypitest
[pypitest]
repository = https://testpypi.python.org/pypi
username = <your user name goes here>
password = <your password goes here>
[pypi]
repository = https://pypi.python.org/pypi
username = <your user name goes here>
password = <your password goes here>
パッケージ登録
$ python setup.py register -r https://testpypi.python.org/pypi
アップロード
$ python setup.py sdist upload -r https://testpypi.python.org/pypi
インストール
$ pip install -i https://testpypi.python.org/pypi <package name>
依存パッケージがあるようのものは「--extra-index-url」を利用すると良い
$ pip install --extra-index-url https://testpypi.python.org/pypi <package name>
setup.cfg:
[upload_docs]
upload-dir = _build/sphinx/html
# Sphinxをビルドしてアップロード
$ python setup.py build_sphinx upload_docs
# アップロード後はpyhthonhosted.orgのURLで確認できる
# pypiからリンクが貼られれます
http://pythonhosted.org/<packge name>/
entry_points = {
"console_scripts": [
"sayhello=sample.commands:hello",
]
}
# sample.commandsモジュールのhello関数を呼び出す
$ pip install sample
$ sayhello
Hello! my name is hoge.
$ which sayhello
/usr/local/bin/sayhello
# リリース時点でタグを切る
$ git tag -am "Version 1.0.0" v1.0.0
$ git push origin --tags
language: python
python: 3.4
env:
matrix:
- TOXENV=py26
- TOXENV=py27
- TOXENV=py33
- TOXENV=py34
- TOXENV=pypy
- TOXENV=flake8
install:
- pip install tox
- if test "$TOXENV" = py34 ; then pip install coveralls ; fi
script: tox
after_script:
- if test "$TOXENV" = py34 ; then coveralls ; fi
install:
- pip install tox
- if test "$TOXENV" = py34 ; then pip install coveralls ; fi
script: tox
after_script:
- if test "$TOXENV" = py34 ; then coveralls ; fi