setup.py例子?

115

学习完这个页面后:

http://docs.python.org/distutils/builtdist.html

我希望找到一些setup.py文件进行学习,以便制作自己的文件 (目标是制作一个Fedora RPM文件)。

社区里有没有好的例子可以指引我?


1
该链接中描述的基于distutils的构建系统已经过时,并将在Python 3.12中被移除。有关如何在2022年开始使用Python打包的最新教程,请参阅Python Packaging User Guide - Christian Long
8个回答

75
完整的编写setup.py脚本的演示可以在这里找到(附有一些示例)。
如果您需要实际的例子,我可以指向一些主要项目的setup.py脚本。 Django的在这里,pyglet的在这里。您可以浏览其他项目的源代码以获取名为setup.py的文件以获取更多示例。
这些不是简单的示例;我提供的教程链接中有那些内容。 这些更复杂,但也更实用。

distutils示例已从Python中删除。在Python 3.10中使用它是不推荐的,并将在Python 3.12中移除。 deprecated in python 3.10 - undefined

37

最小示例

from setuptools import setup, find_packages


setup(
    name="foo",
    version="1.0",
    packages=find_packages(),
)

更多信息请查看文档


31
您可能会发现《Python包管理指南》很有用,尽管它不完整。我建议从Quick Start教程开始。您也可以浏览一下Python Package Index上的Python包。只需下载tarball,解压缩,然后查看setup.py文件即可。或者更好的是,只浏览列出公共源代码存储库(例如GitHub或BitBucket)的包。您肯定会在首页上找到这样的包。
我的最后建议是尝试着自己创建一个包,不要害怕失败。在我自己开始创建包之前,我真的不理解它的含义。在PyPI上创建新包非常简单,删除也同样容易。所以,创建一个虚拟包并尝试调试。

27

首先阅读此内容 https://packaging.python.org/en/latest/current.html

安装工具建议

  1. 使用pip从PyPI安装Python包。
  2. 使用virtualenv或pyvenv来隔离应用程序特定的依赖项,以免影响共享的Python安装。
  3. 使用pip wheel创建wheel分发缓存,以加快后续安装的速度。
  4. 如果您正在寻找完全集成的跨平台软件堆栈管理,请考虑使用buildout(主要面向Web开发社区)、Hashdist或conda(两者都主要面向科学社区)。

打包工具建议

  1. 使用setuptools定义项目并创建源分发。
  2. 使用wheel项目提供的bdist_wheel setuptools扩展来创建wheels。如果您的项目包含二进制扩展,则尤其有益。
  3. 使用twine将分发上传到PyPI。

This answer has aged, and indeed there is a rescue plan for python packaging world called "Wheels way".
I quote pythonwheels.com here:
"What are wheels?" Wheels are the new standard of python distribution and are intended to replace eggs. Support is offered in pip >= 1.4 and setuptools >= 0.8.
Advantages of wheels:
1. Faster installation for pure python and native C extension packages. 2. Avoids arbitrary code execution for installation. (Avoids setup.py) 3. Installation of a C extension does not require a compiler on Windows or OS X. 4. Allows better caching for testing and continuous integration. 5. Creates .pyc files as part of installation to ensure they match the python interpreter used. 6. More consistent installs across platforms and machines.
The full story of correct python packaging (and about wheels) is covered at packaging.python.org.

conda方式

对于科学计算(这也是在packaging.python.org上推荐的),我建议使用CONDA打包,它可以看作是建立在PyPI和pip工具之上的第三方服务。它还可以很好地设置自己版本的binstar,因此我想它可以解决复杂的自定义企业包管理问题。

Conda可以安装到用户文件夹中(不需要超级用户权限),并且可以像魔术一样使用

conda install

以及强大的虚拟环境扩展。


鸡蛋方式

这个选项与python-distribute.org有关,但现在已经过时了(以及该网站),所以让我指向其中一个我喜欢的即用即可的紧凑setup.py示例:

  • 一个非常实用的将脚本和单个Python文件混合到setup.py中的示例/实现给出here
  • 更好的一个来自hyperopt

这句话摘自setup.py状态指南,仍然适用:

  • setup.py消失了!
  • distutils消失了!
  • distribute消失了!
  • pip和virtualenv留下来了!
  • eggs...消失了!

我再添加一点(来自我)

  • wheels
我建议在尝试毫无头绪地复制粘贴之前,先了解packaging-ecosystem(由gotgenes指出的指南)的相关知识。大部分互联网上的示例都是以...开始的。
from distutils.core import setup

但是,例如这个命令不支持构建一个egg包 python setup.py bdist_egg(以及其他一些旧的特性),这些特性在过去是可用的。
from setuptools import setup

And the reason is that they are 不赞成使用的.
现在根据指南,

警告

请使用Distribute包而不是Setuptools包,因为该包存在问题,这些问题无法修复。

不赞成使用的setuptools将被distutils2取代,它将“成为Python 3.3标准库的一部分”。我必须说我喜欢setuptools和eggs,并且还没有完全被distutils2的便利性所说服。它需要
pip install Distutils2

并安装

python -m distutils2.run install

PS

包装从来都不是一件轻松的事情(通过尝试开发新的包装可以学到这一点),因此我认为很多事情都有原因。我只希望这次做得正确。


5
那么,这个答案现在的情况如何?distutils2 是否随 Python 3.3 一起发布?setuptools 是否已经消亡? - Capi Etheriel
你能提供“setup.py状态指南”的参考资料吗?因为“setup.py gone!”是错误的。现在是2017年,setup.py仍然存在。 - karantan

11

我建议使用setup.py作为Python打包用户指南示例项目的设置文件。

Python打包用户指南“旨在成为有关如何使用当前工具打包、发布和安装Python发行版的权威资源”。

然而,截至2022年12月,示例项目已从setup.py切换到pyproject.toml。对于新项目,您可能想考虑做同样的事情。


那是一个很好的例子,而且注释得很好! - wisbucky
1
setup.py 的链接已经失效。 - Maged Saeed
1
@MagedSaeed 这个示例项目已经切换到了pyproject.toml,所以我把链接改成了特定的提交,并添加了一行关于pyproject.toml的说明。 - Razzi Abuissa

6

这是我编写的实用程序,用于生成带有有用注释和链接的简单 setup.py 文件(模板)。我希望它能够有所帮助。

安装

sudo pip install setup-py-cli

用法

只需在终端中键入以下命令即可生成setup.py文件。

setup-py

现在应该在当前目录中出现setup.py文件。

生成的setup.py

from distutils.core import setup
from setuptools import find_packages
import os


# User-friendly description from README.md
current_directory = os.path.dirname(os.path.abspath(__file__))
try:
    with open(os.path.join(current_directory, 'README.md'), encoding='utf-8') as f:
        long_description = f.read()
except Exception:
    long_description = ''

setup(
    # Name of the package
    name=<name of current directory>,

    # Packages to include into the distribution
    packages=find_packages('.'), 

    # Start with a small number and increase it with every change you make
    # https://semver.org
    version='1.0.0',

    # Chose a license from here: https://help.github.com/articles/licensing-a-repository
    # For example: MIT
    license='',

    # Short description of your library
    description='',

    # Long description of your library
    long_description = long_description,
    long_description_context_type = 'text/markdown',

    # Your name
    author='', 

    # Your email
    author_email='',     

    # Either the link to your github or to your website
    url='',

    # Link from which the project can be downloaded
    download_url='',

    # List of keyword arguments
    keywords=[],

    # List of packages to install with this one
    install_requires=[],

    # https://pypi.org/classifiers/
    classifiers=[]  
)

自动生成的setup.py内容:

  • 基于当前目录名称自动填充包名称。
  • 填写一些基本字段。
  • 提供阐述性评论和有用资源链接。
  • 如果没有README.md文件,则自动插入描述信息或空字符串。

这是指向存储库的链接。请随意完善解决方案。


5

请查看这个完整的例子,它是一个小型python包的示例 https://github.com/marcindulak/python-mycli。它基于来自https://packaging.python.org/en/latest/distributing.html的打包建议,使用了distutils中的setup.py,并展示了如何创建RPM和deb包。

项目的setup.py如下所示(有关完整源代码,请参见repo):

#!/usr/bin/env python

import os
import sys

from distutils.core import setup

name = "mycli"

rootdir = os.path.abspath(os.path.dirname(__file__))

# Restructured text project description read from file
long_description = open(os.path.join(rootdir, 'README.md')).read()

# Python 2.4 or later needed
if sys.version_info < (2, 4, 0, 'final', 0):
    raise SystemExit, 'Python 2.4 or later is required!'

# Build a list of all project modules
packages = []
for dirname, dirnames, filenames in os.walk(name):
        if '__init__.py' in filenames:
            packages.append(dirname.replace('/', '.'))

package_dir = {name: name}

# Data files used e.g. in tests
package_data = {name: [os.path.join(name, 'tests', 'prt.txt')]}

# The current version number - MSI accepts only version X.X.X
exec(open(os.path.join(name, 'version.py')).read())

# Scripts
scripts = []
for dirname, dirnames, filenames in os.walk('scripts'):
    for filename in filenames:
        if not filename.endswith('.bat'):
            scripts.append(os.path.join(dirname, filename))

# Provide bat executables in the tarball (always for Win)
if 'sdist' in sys.argv or os.name in ['ce', 'nt']:
    for s in scripts[:]:
        scripts.append(s + '.bat')

# Data_files (e.g. doc) needs (directory, files-in-this-directory) tuples
data_files = []
for dirname, dirnames, filenames in os.walk('doc'):
        fileslist = []
        for filename in filenames:
            fullname = os.path.join(dirname, filename)
            fileslist.append(fullname)
        data_files.append(('share/' + name + '/' + dirname, fileslist))

setup(name='python-' + name,
      version=version,  # PEP440
      description='mycli - shows some argparse features',
      long_description=long_description,
      url='https://github.com/marcindulak/python-mycli',
      author='Marcin Dulak',
      author_email='X.Y@Z.com',
      license='ASL',
      # https://pypi.python.org/pypi?%3Aaction=list_classifiers
      classifiers=[
          'Development Status :: 1 - Planning',
          'Environment :: Console',
          'License :: OSI Approved :: Apache Software License',
          'Natural Language :: English',
          'Operating System :: OS Independent',
          'Programming Language :: Python :: 2',
          'Programming Language :: Python :: 2.4',
          'Programming Language :: Python :: 2.5',
          'Programming Language :: Python :: 2.6',
          'Programming Language :: Python :: 2.7',
          'Programming Language :: Python :: 3',
          'Programming Language :: Python :: 3.2',
          'Programming Language :: Python :: 3.3',
          'Programming Language :: Python :: 3.4',
      ],
      keywords='argparse distutils cli unittest RPM spec deb',
      packages=packages,
      package_dir=package_dir,
      package_data=package_data,
      scripts=scripts,
      data_files=data_files,
      )

一个遵循 Fedora/EPEL 打包指南的 RPM 规范文件可能如下所示:

# Failsafe backport of Python2-macros for RHEL <= 6
%{!?python_sitelib: %global python_sitelib      %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())")}
%{!?python_sitearch:    %global python_sitearch     %(%{__python} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(1))")}
%{!?python_version: %global python_version      %(%{__python} -c "import sys; sys.stdout.write(sys.version[:3])")}
%{!?__python2:      %global __python2       %{__python}}
%{!?python2_sitelib:    %global python2_sitelib     %{python_sitelib}}
%{!?python2_sitearch:   %global python2_sitearch    %{python_sitearch}}
%{!?python2_version:    %global python2_version     %{python_version}}

%{!?python2_minor_version: %define python2_minor_version %(%{__python} -c "import sys ; print sys.version[2:3]")}

%global upstream_name mycli


Name:           python-%{upstream_name}
Version:        0.0.1
Release:        1%{?dist}
Summary:        A Python program that demonstrates usage of argparse
%{?el5:Group:       Applications/Scientific}
License:        ASL 2.0

URL:            https://github.com/marcindulak/%{name}
Source0:        https://github.com/marcindulak/%{name}/%{name}-%{version}.tar.gz

%{?el5:BuildRoot:   %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)}
BuildArch:      noarch

%if 0%{?suse_version}
BuildRequires:      python-devel
%else
BuildRequires:      python2-devel
%endif


%description
A Python program that demonstrates usage of argparse.


%prep
%setup -qn %{name}-%{version}


%build
%{__python2} setup.py build


%install
%{?el5:rm -rf $RPM_BUILD_ROOT}
%{__python2} setup.py install --skip-build --prefix=%{_prefix} \
   --optimize=1 --root $RPM_BUILD_ROOT


%check
export PYTHONPATH=`pwd`/build/lib
export PATH=`pwd`/build/scripts-%{python2_version}:${PATH}
%if 0%{python2_minor_version} >= 7
%{__python2} -m unittest discover -s %{upstream_name}/tests -p '*.py'
%endif


%clean
%{?el5:rm -rf $RPM_BUILD_ROOT}


%files
%doc LICENSE README.md
%{_bindir}/*
%{python2_sitelib}/%{upstream_name}
%{?!el5:%{python2_sitelib}/*.egg-info}


%changelog
* Wed Jan 14 2015 Marcin Dulak <X.Y@Z.com> - 0.0.1-1
- initial version

2
请不要简单地复制/粘贴链接,而是尝试提取实际上回答问题的重要部分。 - fredmaggiowski

3

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接