标签归档:setuptools

如何在setuptools / distribute中包含软件包数据?

问题:如何在setuptools / distribute中包含软件包数据?

使用setuptools / distribute时,我无法使安装程序提取任何package_data文件。我读过的所有内容都表明,以下是正确的方法。有人可以请教吗?

setup(
   name='myapp',
   packages=find_packages(),
   package_data={
      'myapp': ['data/*.txt'],
   },
   include_package_data=True,
   zip_safe=False,
   install_requires=['distribute'],
)

myapp/data/数据文件的位置在哪里。

When using setuptools, I can not get the installer to pull in any package_data files. Everything I’ve read says that the following is the correct way to do it. Can someone please advise?

setup(
   name='myapp',
   packages=find_packages(),
   package_data={
      'myapp': ['data/*.txt'],
   },
   include_package_data=True,
   zip_safe=False,
   install_requires=['distribute'],
)

where myapp/data/ is the location of the data files.


回答 0

我知道这是一个老问题,但人们发现这里通过谷歌自己的方式: package_data是低了下来,肮脏的谎言。它仅在构建二进制软件包(python setup.py bdist ...)时使用,在构建源软件包(python setup.py sdist ...)时不使用。当然,这是荒谬的-人们希望构建源代码分发将导致文件集合,这些文件可以发送给其他人来构建二进制分发。

在任何情况下,使用MANIFEST.in将工作二进制和源分布。

I realize that this is an old question, but for people finding their way here via Google: package_data is a low-down, dirty lie. It is only used when building binary packages (python setup.py bdist ...) but not when building source packages (python setup.py sdist ...). This is, of course, ridiculous — one would expect that building a source distribution would result in a collection of files that could be sent to someone else to built the binary distribution.

In any case, using MANIFEST.in will work both for binary and for source distributions.


回答 1

我只是有同样的问题。解决的方法是简单地删除include_package_data=True

这里阅读之后,我意识到它include_package_data旨在包含来自版本控制的文件,而不是顾名思义仅包含“ include package data”。从文档:

[include_package_data]的数据文件必须处于CVS或Subversion控制之下

如果要对包含的文件进行更细粒度的控制(例如,如果您的软件包目录中有文档文件,并希望将其从安装中排除),则也可以使用package_data关键字。

把那个参数排除掉可以解决这个问题,这恰好是为什么当您切换到distutils时它也可以工作的原因,因为它不接受那个参数。

I just had this same issue. The solution, was simply to remove include_package_data=True.

After reading here, I realized that include_package_data aims to include files from version control, as opposed to merely “include package data” as the name implies. From the docs:

The data files [of include_package_data] must be under CVS or Subversion control

If you want finer-grained control over what files are included (for example, if you have documentation files in your package directories and want to exclude them from installation), then you can also use the package_data keyword.

Taking that argument out fixed it, which is coincidentally why it also worked when you switched to distutils, since it doesn’t take that argument.


回答 2

遵循@Joe的建议删除该include_package_data=True行也对我有用。

详细说明一下,我没有 MANIFEST.in文件。我使用Git而不是CVS。

存储库采用以下形式:

/myrepo
    - .git/
    - setup.py
    - myproject
        - __init__.py
        - some_mod
            - __init__.py
            - animals.py
            - rocks.py
        - config
            - __init__.py
            - settings.py
            - other_settings.special
            - cool.huh
            - other_settings.xml
        - words
            - __init__.py
            word_set.txt

setup.py

from setuptools import setup, find_packages
import os.path

setup (
    name='myproject',
    version = "4.19",
    packages = find_packages(),  
    # package_dir={'mypkg': 'src/mypkg'},  # didnt use this.
    package_data = {
        # If any package contains *.txt or *.rst files, include them:
        '': ['*.txt', '*.xml', '*.special', '*.huh'],
    },

#
    # Oddly enough, include_package_data=True prevented package_data from working.
    # include_package_data=True, # Commented out.
    data_files=[
#               ('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
        ('/opt/local/myproject/etc', ['myproject/config/settings.py', 'myproject/config/other_settings.special']),
        ('/opt/local/myproject/etc', [os.path.join('myproject/config', 'cool.huh')]),
#
        ('/opt/local/myproject/etc', [os.path.join('myproject/config', 'other_settings.xml')]),
        ('/opt/local/myproject/data', [os.path.join('myproject/words', 'word_set.txt')]),
    ],

    install_requires=[ 'jsonschema',
        'logging', ],

     entry_points = {
        'console_scripts': [
            # Blah...
        ], },
)

python setup.py sdist为源发行版(没有尝试过二进制)运行。

在新的虚拟环境中,我有一个myproject-4.19.tar.gz文件,并且我使用

(venv) pip install ~/myproject-4.19.tar.gz
...

除了将所有内容都安装到我的虚拟环境中之外site-packages,这些特殊数据文件也都安装到/opt/local/myproject/data和中/opt/local/myproject/etc

Following @Joe ‘s recommendation to remove the include_package_data=True line also worked for me.

To elaborate a bit more, I have no MANIFEST.in file. I use Git and not CVS.

Repository takes this kind of shape:

/myrepo
    - .git/
    - setup.py
    - myproject
        - __init__.py
        - some_mod
            - __init__.py
            - animals.py
            - rocks.py
        - config
            - __init__.py
            - settings.py
            - other_settings.special
            - cool.huh
            - other_settings.xml
        - words
            - __init__.py
            word_set.txt

setup.py:

from setuptools import setup, find_packages
import os.path

setup (
    name='myproject',
    version = "4.19",
    packages = find_packages(),  
    # package_dir={'mypkg': 'src/mypkg'},  # didnt use this.
    package_data = {
        # If any package contains *.txt or *.rst files, include them:
        '': ['*.txt', '*.xml', '*.special', '*.huh'],
    },

#
    # Oddly enough, include_package_data=True prevented package_data from working.
    # include_package_data=True, # Commented out.
    data_files=[
#               ('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
        ('/opt/local/myproject/etc', ['myproject/config/settings.py', 'myproject/config/other_settings.special']),
        ('/opt/local/myproject/etc', [os.path.join('myproject/config', 'cool.huh')]),
#
        ('/opt/local/myproject/etc', [os.path.join('myproject/config', 'other_settings.xml')]),
        ('/opt/local/myproject/data', [os.path.join('myproject/words', 'word_set.txt')]),
    ],

    install_requires=[ 'jsonschema',
        'logging', ],

     entry_points = {
        'console_scripts': [
            # Blah...
        ], },
)

I run python setup.py sdist for a source distrib (haven’t tried binary).

And when inside of a brand new virtual environment, I have a myproject-4.19.tar.gz, file, and I use

(venv) pip install ~/myproject-4.19.tar.gz
...

And other than everything getting installed to my virtual environment’s site-packages, those special data files get installed to /opt/local/myproject/data and /opt/local/myproject/etc.


回答 3

include_package_data=True 为我工作。

如果你使用git,请记住,包括setuptools-gitinstall_requires。远没有拥有Manifest或包含所有路径package_data(在我的情况下,它是具有各种静态特性的django应用程序)那么无聊

(粘贴了我的评论,就像k3-rnc所说的那样,实际上是有帮助的)

include_package_data=True worked for me.

If you use git, remember to include setuptools-git in install_requires. Far less boring than having a Manifest or including all path in package_data ( in my case it’s a django app with all kind of statics )

( pasted the comment I made, as k3-rnc mentioned it’s actually helpful as is )


回答 4

更新:此答案是旧的,该信息不再有效。所有setup.py配置均应使用import setuptools。我在https://stackoverflow.com/a/49501350/64313中添加了更完整的答案


我通过切换到distutils解决了这个问题。似乎已弃用和/或破坏了分发。

from distutils.core import setup

setup(
   name='myapp',
   packages=['myapp'],
   package_data={
      'myapp': ['data/*.txt'],
   },
)

Update: This answer is old and the information is no longer valid. All setup.py configs should use import setuptools. I’ve added a more complete answer at https://stackoverflow.com/a/49501350/64313


I solved this by switching to distutils. Looks like distribute is deprecated and/or broken.

from distutils.core import setup

setup(
   name='myapp',
   packages=['myapp'],
   package_data={
      'myapp': ['data/*.txt'],
   },
)

回答 5

古老的问题,然而… python的软件包管理确实有很多不足之处。因此,我有在本地使用pip安装到指定目录的用例,很惊讶package_data和data_files路径都无法解决。我不希望再向仓库添加另一个文件,所以最终我利用了data_files和setup.py选项–install-data;。像这样的东西

pip install . --install-option="--install-data=$PWD/package" -t package  

Ancient question and yet… package management of python really leaves a lot to be desired. So I had the use case of installing using pip locally to a specified directory and was surprised both package_data and data_files paths did not work out. I was not keen on adding yet another file to the repo so I ended up leveraging data_files and setup.py option –install-data; something like this

pip install . --install-option="--install-data=$PWD/package" -t package  

回答 6

将包含软件包数据的文件夹移到module文件夹为我解决了这个问题。

看到这个问题:MANIFEST.in在“ python setup.py install”上被忽略-没有安装数据文件?

Moving the folder containing the package data into to module folder solved the problem for me.

See this question: MANIFEST.in ignored on “python setup.py install” – no data files installed?


回答 7

我在几天中遇到了同样的问题,但是即使一切都变得混乱,这个线程也无法为我提供帮助。因此,我进行了研究,发现了以下解决方案:

基本上在这种情况下,您应该执行以下操作:

from setuptools import setup

setup(
   name='myapp',
   packages=['myapp'],
   package_dir={'myapp':'myapp'}, # the one line where all the magic happens
   package_data={
      'myapp': ['data/*.txt'],
   },
)

完整的其他stackoverflow答案在这里

I had the same problem for a couple of days but even this thread wasn’t able to help me as everything was confusing. So I did my research and found the following solution:

Basically in this case, you should do:

from setuptools import setup

setup(
   name='myapp',
   packages=['myapp'],
   package_dir={'myapp':'myapp'}, # the one line where all the magic happens
   package_data={
      'myapp': ['data/*.txt'],
   },
)

The full other stackoverflow answer here


回答 8

只需删除该行:

include_package_data=True,

从您的安装脚本中,它将正常工作。(刚刚通过最新的setuptools测试。)

Just remove the line:

include_package_data=True,

from your setup script, and it will work fine. (Tested just now with latest setuptools.)


回答 9

使用setup.cfg(setuptools≥30.3.0)

从setuptools 30.3.0(2016年12月8日发布)开始,您可以保持setup.py很小的规模并将配置移动到setup.cfg文件中。使用这种方法,您可以将包数据放在以下[options.package_data]部分中:

[options.package_data]
* = *.txt, *.rst
hello = *.msg

在这种情况下,您setup.py可以做到:

from setuptools import setup
setup()

有关更多信息,请参阅使用setup.cfg文件配置安装程序

一些关于setup.cfgPEP 518中pyproject.toml提议的弃用赞成的说法,但从2020年2月21日起这仍然是临时的。

Using setup.cfg (setuptools ≥ 30.3.0)

Starting with setuptools 30.3.0 (released 2016-12-08), you can keep your setup.py very small and move the configuration to a setup.cfg file. With this approach, you could put your package data in an [options.package_data] section:

[options.package_data]
* = *.txt, *.rst
hello = *.msg

In this case, your setup.py can be as short as:

from setuptools import setup
setup()

For more information, see configuring setup using setup.cfg files.

There is some talk of deprecating setup.cfg in favour of pyproject.toml as proposed in PEP 518, but this is still provisional as of 2020-02-21.


在64位Windows上安装SetupTools

问题:在64位Windows上安装SetupTools

我在Windows 7 64位系统上运行Python 2.7,当我运行setuptools的安装程序时,它告诉我未安装Python 2.7。具体的错误消息是:

`Python Version 2.7 required which was not found in the registry`

我安装的Python版本是:

`Python 2.7 (r27:82525, Jul  4 2010, 07:43:08) [MSC v.1500 64 bit (AMD64)] on win32`

我正在看setuptools网站,它没有提到64位Windows的任何安装程序。我错过了什么吗,还是必须从源代码安装它?

I’m running Python 2.7 on Windows 7 64-bit, and when I run the installer for setuptools it tells me that Python 2.7 is not installed. The specific error message is:

`Python Version 2.7 required which was not found in the registry`

My installed version of Python is:

`Python 2.7 (r27:82525, Jul  4 2010, 07:43:08) [MSC v.1500 64 bit (AMD64)] on win32`

I’m looking at the setuptools site and it doesn’t mention any installers for 64-bit Windows. Have I missed something or do I have to install this from source?


回答 0

显然(在OS X上面临相关的64位和32位问题),Windows安装程序中存在一个错误。我偶然发现了这种解决方法,它可能会有所帮助-基本上,您可以创建自己的注册表值,HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.6\InstallPath然后从中复制InstallPath值HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.6\InstallPath。有关更多详细信息,请参见下面的答案。

如果执行此操作,请注意setuptools 只能安装32位库

注意:以下回复提供了更多详细信息,因此也请阅读它们。

Apparently (having faced related 64- and 32-bit issues on OS X) there is a bug in the Windows installer. I stumbled across this workaround, which might help – basically, you create your own registry value HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.6\InstallPath and copy over the InstallPath value from HKEY_LOCAL_MACHINE\SOFTWARE\Python\PythonCore\2.6\InstallPath. See the answer below for more details.

If you do this, beware that setuptools may only install 32-bit libraries.

NOTE: the responses below offer more detail, so please read them too.


回答 1

问题:您有64位Python和32位安装程序。这将导致扩展模块出现问题。

安装程序找不到Python的原因是Windows 7提供的透明32位仿真。64位和32位程序将写入Windows注册表的不同部分。

64位: HKLM|HKCU\SOFTWARE\

32位:HKLM|HKCU\SOFTWARE\wow6432node\

这意味着64位Python安装程序会写入HKLM\SOFTWARE\Python,而32位setuptools安装程序会查看HKLM\SOFTWARE\wow6432node\Python(这是由Windows自动处理的,程序不会注意到)。这是预期的行为,而不是错误。

通常,您有以下选择:

  • “干净”的方式:如果必须使用32位模块或扩展,请使用32位Python
  • 另一种“干净”的方式:仅在使用64位Python时才使用64位安装程序(请参见下文)
  • 上面的答案表明了什么:复制HKLM\SOFTWARE\PythonHKLM\SOFTWARE\wow6432node\Python,但这导致二进制分发出现问题,因为64位Python无法加载32位编译模块(请勿这样做!)
  • 使用setuptools而不是distutils安装程序(easy_install或pip)安装纯Python模块

例如,对于setuptools本身,您不能将32位安装程序用于64位Python,因为它包含二进制文件。但是在http://www.lfd.uci.edu/~gohlke/pythonlibs/中有一个64位安装程序(其他模块也有许多安装程序)。如今,PyPi上的许多软件包都有二进制发行版,因此您可以通过pip安装它们。

Problem: you have 64-bit Python, and a 32-bit installer. This will cause problems for extension modules.

The reasons why the installer doesn’t finds Python is the transparent 32-bit emulation from Windows 7. 64-bit and 32-bit programs will write to different parts of the Windows registry.

64-bit: HKLM|HKCU\SOFTWARE\

32-bit: HKLM|HKCU\SOFTWARE\wow6432node\.

This means that the 64-bit Python installer writes to HKLM\SOFTWARE\Python, but the 32-bit setuptools installer looks at HKLM\SOFTWARE\wow6432node\Python (this is handled by windows automatically, programs don’t notice). This is expected behavior and not a bug.

Usually, you have these choices:

  • the “clean” way: use 32-bit Python if you have to use 32-bit modules or extensions
  • the other “clean” way: only use 64-bit installers when using 64-bit Python (see below)
  • what the answer above suggests: copy HKLM\SOFTWARE\Python to HKLM\SOFTWARE\wow6432node\Python, but this will cause problems with binary distributions, as 64-bit Python can’t load 32-bit compiled modules (do NOT do this!)
  • install pure Python modules with setuptools instead of the distutils installer (easy_install or pip)

For setuptools itself, for example, you can’t use a 32-bit installer for 64-bit Python as it includes binary files. But there’s a 64-bit installer at http://www.lfd.uci.edu/~gohlke/pythonlibs/ (has many installers for other modules too). Nowadays, many packages on PyPi have binary distributions, so you can install them via pip.


回答 2

我做了一个注册表(.reg)文件,它将自动为您更改注册表。如果安装在“ C:\ Python27”中,它将起作用:

下载32位版本 HKEY_LOCAL_MACHINE|HKEY_CURRENT_USER\SOFTWARE\wow6432node\

下载64位版本 HKEY_LOCAL_MACHINE|HKEY_CURRENT_USER\SOFTWARE\

I made a registry (.reg) file that will automatically change the registry for you. It works if it’s installed in “C:\Python27”:

Download 32-bit version HKEY_LOCAL_MACHINE|HKEY_CURRENT_USER\SOFTWARE\wow6432node\

Download 64-bit version HKEY_LOCAL_MACHINE|HKEY_CURRENT_USER\SOFTWARE\


回答 3

是的,您是对的,问题出在setuptools的64位Python和32位安装程序上。

在Windows上安装64位setuptools的最佳方法是将ez_setup.py下载到C:\ Python27 \ Scripts并运行它。它将下载用于setuptools的适当的64位.egg文件并为您安装。

资料来源:http : //pypi.python.org/pypi/setuptools

PS我建议您不要使用第三方的64位.exe setuptools安装程序或操纵注册表

Yes, you are correct, the issue is with 64-bit Python and 32-bit installer for setuptools.

The best way to get 64-bit setuptools installed on Windows is to download ez_setup.py to C:\Python27\Scripts and run it. It will download appropriate 64-bit .egg file for setuptools and install it for you.

Source: http://pypi.python.org/pypi/setuptools

P.S. I’d recommend against using 3rd party 64-bit .exe setuptools installers or manipulating registry


回答 4

创建一个名为python2.7.reg(注册表文件)的文件,并将其内容放入其中:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7]

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\Help]

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\Help\MainPythonDocumentation]
@="C:\\Python27\\Doc\\python26.chm"

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\InstallPath]
@="C:\\Python27\\"

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\InstallPath\InstallGroup]
@="Python 2.7"

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\Modules]

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\PythonPath]
@="C:\\Python27\\Lib;C:\\Python27\\DLLs;C:\\Python27\\Lib\\lib-tk"

并确保每条路径都是正确的!

然后运行(合并)并完成:)

Create a file named python2.7.reg (registry file) and put this content into it:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7]

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\Help]

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\Help\MainPythonDocumentation]
@="C:\\Python27\\Doc\\python26.chm"

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\InstallPath]
@="C:\\Python27\\"

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\InstallPath\InstallGroup]
@="Python 2.7"

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\Modules]

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.7\PythonPath]
@="C:\\Python27\\Lib;C:\\Python27\\DLLs;C:\\Python27\\Lib\\lib-tk"

And make sure every path is right!

Then run (merge) it and done :)


回答 5

register.py这个要点获取文件。将其保存在您的C驱动器或D驱动器上,转到CMD以运行它:

'python register.py'

然后,您将能够安装它。

Get the file register.py from this gist. Save it on your C drive or D drive, go to CMD to run it with:

'python register.py'

Then you will be able to install it.


回答 6

对于Windows上的64位Python,请下载ez_setup.py并运行它;它将下载适当的.egg文件并为您安装。

由于distutils安装程序兼容性问题,在撰写本文时,.exe安装程序不支持Windows的64位版本的Python 。

For 64-bit Python on Windows download ez_setup.py and run it; it will download the appropriate .egg file and install it for you.

At the time of writing the .exe installer does not support 64-bit versions of Python for Windows, due to a distutils installer compatibility issue.


回答 7

要允许Windows安装程序在Windows 7Windows 7中找到已安装的Python目录,请更改要将安装程序安装到的Python安装,然后将已安装的路径添加到InstallPath注册表项的(默认)值中:

HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Python \ PythonCore \ 2.X \ InstallPath

其中“ X ”是Python版本(即2.5、2.6或2.7)。

To allow Windows installers to find the installed Python directory in Windows 7, OR, change which Python installation to install an installer into, add the installed path into the InstallPath registry key’s (Default) value:

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Python\PythonCore\2.X\InstallPath

Where “X” is the Python version (that is, 2.5, 2.6, or 2.7).


回答 8

我尝试了上述操作,但将注册表项添加到LOCALMACHINE并没有完成任务。因此,如果您仍然被卡住,请尝试此操作。

Windows注册表编辑器版本5.00

[HKEY_CURRENT_USER \ SOFTWARE \ Python]

[HKEY_CURRENT_USER \ SOFTWARE \ Python \ PythonCore]

[HKEY_CURRENT_USER \ SOFTWARE \ Python \ PythonCore \ 2.7]

[HKEY_CURRENT_USER \ SOFTWARE \ Python \ PythonCore \ 2.7 \ Help]

[HKEY_CURRENT_USER \ SOFTWARE \ Python \ PythonCore \ 2.7 \ Help \ Main Python文档] @ =“ C:\ Python27 \ Doc \ python272.chm”

[HKEY_CURRENT_USER \ SOFTWARE \ Python \ PythonCore \ 2.7 \ InstallPath] @ =“ C:\ Python27 \”

[HKEY_CURRENT_USER \ SOFTWARE \ Python \ PythonCore \ 2.7 \ InstallPath \ InstallGroup] @ =“ Python 2.7”

[HKEY_CURRENT_USER \ SOFTWARE \ Python \ PythonCore \ 2.7 \ Modules]

[HKEY_CURRENT_USER \ SOFTWARE \ Python \ PythonCore \ 2.7 \ PythonPath] @ =“ C:\ Python27 \ Lib; C:\ Python27 \ DLLs; C:\ Python27 \ Lib \ lib-tk”

将以上内容复制粘贴到记事本中,并将其另存为Python27.reg。现在,按照上述答案中的说明运行/合并文件。(请确保根据您的安装更正了Python安装路径。

上面的答案对本地用户的建议对当前用户来说确实很简单。

I tried the above and adding the registry keys to the LOCALMACHINE was not getting the job done. So in case you are still stuck , try this.

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\SOFTWARE\Python]

[HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore]

[HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\2.7]

[HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\2.7\Help]

[HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\2.7\Help\Main Python Documentation] @=”C:\Python27\Doc\python272.chm”

[HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\2.7\InstallPath] @=”C:\Python27\”

[HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\2.7\InstallPath\InstallGroup] @=”Python 2.7″

[HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\2.7\Modules]

[HKEY_CURRENT_USER\SOFTWARE\Python\PythonCore\2.7\PythonPath] @=”C:\Python27\Lib;C:\Python27\DLLs;C:\Python27\Lib\lib-tk”

Copy paste the above in notepad and save it as Python27.reg . Now run/merge the file as mentioned in the answers above. (Make sure the paths of Python installation are corrected as per your installation.

It simply does ,what the above answers suggest for a local machine ,to the current user.


回答 9

这是另一个帖子/主题的链接。我能够运行此脚本来自动注册python 2.7。(确保从.exe要注册的Python 2.x运行它!)

要注册Python 3.x,我必须修改print语法并导入winreg(而不是_winreg),然后运行Python 3 .exe

https://stackoverflow.com/a/29633714/3568893

Here is a link to another post/thread. I was able run this script to automate registration of Python 2.7. (Make sure to run it from the Python 2.x .exe you want to register!)

To register Python 3.x I had to modify the print syntax and import winreg (instead of _winreg), then run the Python 3 .exe.

https://stackoverflow.com/a/29633714/3568893


回答 10

您可以在此处找到许多库的64位安装程序:http : //www.lfd.uci.edu/~gohlke/pythonlibs/

You can find 64bit installers for a lot of libs here: http://www.lfd.uci.edu/~gohlke/pythonlibs/


python项目需要MANIFEST.in吗,应该在其中吗?

问题:python项目需要MANIFEST.in吗,应该在其中吗?

“ Python分发”指南(位于python-distribute.org,但注册已失败)告诉我包括doc/txt文件,并且.py文件中排除了MANIFEST.in文件

sourcedist文档告诉我只有sdist用途MANIFEST.in,并只包括文件指定和包含.py文件。它还告诉我使用:python setup.py sdist --manifest-only生成一个MANIFEST,但是python告诉我这不存在

我很欣赏它们来自不同版本的python,并且分发系统完全混乱,但是假设我使用的是python 3,并且setuptools(新的包括distribution,但现在称为setuptools,而不是不赞成仅用于分发工具的旧setuptools重新分配到发行版,并将发行版重命名为setuptools …..)

我遵循的是“标准”文件夹结构和setup.py文件,

  1. 我需要一个MANIFEST.in吗?
  2. 应该是什么?
  3. 所有这些不同的包装系统和方法何时才能形成一个简单的过程?

The “Python Distribute” guide (was at python-distribute.org, but that registration has lapsed) tells me to include doc/txt files and .py files are excluded in MANIFEST.in file

The sourcedist documentation tells me only sdist uses MANIFEST.in and only includes file you specify and to include .py files. It also tells me to use: python setup.py sdist --manifest-only to generate a MANIFEST, but python tells me this doesn’t exist

I appreciate these are from different versions of python and the distribution system is in a complete mess, but assuming I am using python 3 and setuptools (the new one that includes distribute but now called setuptools, not the old setuptools that was deprecated for distribute tools only to be brought back into distribute and distribute renamed to setuptools…..)

and I’m following the ‘standard’ folder structure and setup.py file,

  1. Do I need a MANIFEST.in ?
  2. What should be in it ?
  3. When will all these different package systems and methods be made into one single simple process ?

回答 0

回复:“我需要一个MANIFEST.in吗?

不,您不必使用MANIFEST.in。两者,distutils并且setuptools被包括在源分发包中提到的所有文件setup.py-的模块,包Python文件, README.txttest/test*.py。如果这是分发包中所需的全部内容,则不必使用MANIFEST.in

如果要操纵(添加或删除)要包括的默认文件,则必须使用MANIFEST.in

回复:里面应该有什么?

程序很简单:

  1. 确保在您中setup.py(包含setup参数)包含对程序运行至关重要的所有文件(模块,程序包,脚本…)

  2. 澄清一下,是否有要添加的文件或要排除的文件。如果都不需要,则无需使用MANIFEST.in

  3. 如果MANIFEST.in需要,请创建它。通常情况下,你添加有tests*/*.py文件,README.rst如果你不使用README.txtdocs如果有必要的文件,可能还有一些数据文件的测试套件。

例如:

include README.rst
include COPYING.txt

要对其进行测试,请运行python setup.py sdist并检查在下创建的tarball dist/

这些不同的包装系统何时会…

比较今天和2年前的情况-情况要好得多- setuptools是要走的路。您可以忽略一个事实,distutils它有点破损,并且是低级别的基础,setuptools因此setuptools请注意对您隐藏这些东西。

编辑:我使用的最后几个项目pbr用于构建分发包,其中三行setup.py,其余部分在setup.cfg和中requirements.txt。无需关心MANIFEST.in和其他奇怪的东西。即使该软件包值得更多文档。参见http://docs.openstack.org/developer/pbr/

Re: “Do I need a MANIFEST.in?

No, you do not have to use MANIFEST.in. Both, distutils and setuptools are including in source distribution package all the files mentioned in setup.py – modules, package python files, README.txt and test/test*.py. If this is all you want to have in distribution package, you do not have to use MANIFEST.in.

If you want to manipulate (add or remove) default files to include, you have to use MANIFEST.in.

Re: What should be in it?

The procedure is simple:

  1. Make sure, in your setup.py you include (by means of setup arguments) all the files you feel important for the program to run (modules, packages, scripts …)

  2. Clarify, if there are some files to add or some files to exclude. If neither is needed, then there is no need for using MANIFEST.in.

  3. If MANIFEST.in is needed, create it. Usually, you add there tests*/*.py files, README.rst if you do not use README.txt, docs files and possibly some data files for test suite, if necessary.

For example:

include README.rst
include COPYING.txt

To test it, run python setup.py sdist, and examine the tarball created under dist/.

When will all these different package systems …

Comparing the situation today and 2 years ago – the situation is much much better – setuptools is the way to go. You can ignore the fact, distutils is a bit broken and is low level base for setuptools as setuptools shall take care of hiding these things from you.

EDIT: Last few projects I use pbr for building distribution packages with three line setup.py and rest being in setup.cfg and requirements.txt. No need to care about MANIFEST.in and other strange stuff. Even though the package would deserve a bit more documentation. See http://docs.openstack.org/developer/pbr/


回答 1

旧问题,新答案:

不,您不需要MANIFEST.in。但是,要setuptools执行您(通常)的意思,您确实需要使用setuptools_scm,它MANIFEST.in在2个关键位置扮演角色:

  • 它确保在运行sdist命令时打包所有相关文件(其中所有相关文件都定义为“源代码控制下的所有文件”)
  • include_package_data用于将包数据包括为build或的一部分时bdist_wheel。(再次:在源代码控制下的文件)

历史上的理解MANIFEST.in是:当您没有源代码控制系统时,您需要其他机制来区分“源文件”和“碰巧在您的工作目录中的文件”。但是,您的项目处于源代码控制之下(正确吗?),因此不需要MANIFEST.in本文中的更多信息

Old question, new answer:

No, you don’t need MANIFEST.in. However, to get setuptools to do what you (usually) mean, you do need to use the setuptools_scm, which takes the role of MANIFEST.in in 2 key places:

  • It ensures all relevant files are packaged when running the sdist command (where all relevant files is defined as “all files under source control”)
  • When using include_package_data to include package data as part of the build or bdist_wheel. (again: files under source control)

The historical understanding of MANIFEST.in is: when you don’t have a source control system, you need some other mechanism to distinguish between “source files” and “files that happen to be in your working directory”. However, your project is under source control (right??) so there’s no need for MANIFEST.in. More info in this article.


使用Python setuptools的安装后脚本

问题:使用Python setuptools的安装后脚本

是否可以在setuptools setup.py文件中指定安装后的Python脚本文件,以便用户可以运行以下命令:

python setup.py install

在本地项目文件存档上,或

pip install <name>

对于PyPI项目,该脚本将在标准setuptools安装完成时运行吗?我希望执行可以在单个Python脚本文件中编码的安装后任务(例如,向用户传递自定义安装后消息,从其他远程源存储库中提取其他数据文件)。

几年前,我遇到了这个SO答案,它回答了该主题,听起来好像当时的共识是您需要创建一个install子命令。如果仍然是这种情况,是否可以有人提供如何执行此操作的示例,以便用户不必输入第二条命令来运行脚本?

Is it possible to specify a post-install Python script file as part of the setuptools setup.py file so that a user can run the command:

python setup.py install

on a local project file archive, or

pip install <name>

for a PyPI project and the script will be run at the completion of the standard setuptools install? I am looking to perform post-install tasks that can be coded in a single Python script file (e.g. deliver a custom post-install message to the user, pull additional data files from a different remote source repository).

I came across this SO answer from several years ago that addresses the topic and it sounds as though the consensus at that time was that you need to create an install subcommand. If that is still the case, would it be possible for someone to provide an example of how to do this so that it is not necessary for the user to enter a second command to run the script?


回答 0

注意:以下解决方案仅在安装源分发zip或tarball或从源树以可编辑模式安装时有效。从二元轮()安装时将无法使用.whl


此解决方案更加透明:

您将添加一些内容,setup.py并且不需要额外的文件。

另外,您还需要考虑两种不同的后安装方式。一个用于开发/可编辑模式,另一个用于安装模式。

将这两个包含安装后脚本的类添加到setup.py

from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install


class PostDevelopCommand(develop):
    """Post-installation for development mode."""
    def run(self):
        develop.run(self)
        # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION

class PostInstallCommand(install):
    """Post-installation for installation mode."""
    def run(self):
        install.run(self)
        # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION

cmdclasssetup()函数中插入参数setup.py

setup(
    ...

    cmdclass={
        'develop': PostDevelopCommand,
        'install': PostInstallCommand,
    },

    ...
)

您甚至可以在安装过程中调用shell命令,例如在本示例中进行安装前准备工作:

from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install
from subprocess import check_call


class PreDevelopCommand(develop):
    """Pre-installation for development mode."""
    def run(self):
        check_call("apt-get install this-package".split())
        develop.run(self)

class PreInstallCommand(install):
    """Pre-installation for installation mode."""
    def run(self):
        check_call("apt-get install this-package".split())
        install.run(self)


setup(
    ...

PS:setuptools上没有任何预安装入口点。如果您想知道为什么没有,请阅读此讨论

Note: The solution below only works when installing a source distribution zip or tarball, or installing in editable mode from a source tree. It will not work when installing from a binary wheel (.whl)


This solution is more transparent:

You will make a few additions to setup.py and there is no need for an extra file.

Also you need to consider two different post-installations; one for development/editable mode and the other one for install mode.

Add these two classes that includes your post-install script to setup.py:

from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install


class PostDevelopCommand(develop):
    """Post-installation for development mode."""
    def run(self):
        develop.run(self)
        # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION

class PostInstallCommand(install):
    """Post-installation for installation mode."""
    def run(self):
        install.run(self)
        # PUT YOUR POST-INSTALL SCRIPT HERE or CALL A FUNCTION

and insert cmdclass argument to setup() function in setup.py:

setup(
    ...

    cmdclass={
        'develop': PostDevelopCommand,
        'install': PostInstallCommand,
    },

    ...
)

You can even call shell commands during installation, like in this example which does pre-installation preparation:

from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install
from subprocess import check_call


class PreDevelopCommand(develop):
    """Pre-installation for development mode."""
    def run(self):
        check_call("apt-get install this-package".split())
        develop.run(self)

class PreInstallCommand(install):
    """Pre-installation for installation mode."""
    def run(self):
        check_call("apt-get install this-package".split())
        install.run(self)


setup(
    ...

P.S. there are no any pre-install entry points available on setuptools. Read this discussion if you are wondering why there is none.


回答 1

注意:以下解决方案仅在安装源分发zip或tarball或从源树以可编辑模式安装时有效。从二元轮()安装时将无法使用.whl


当安装后脚本要求已安装软件包依赖项时,这是对我有用的唯一策略:

import atexit
from setuptools.command.install import install


def _post_install():
    print('POST INSTALL')


class new_install(install):
    def __init__(self, *args, **kwargs):
        super(new_install, self).__init__(*args, **kwargs)
        atexit.register(_post_install)


setuptools.setup(
    cmdclass={'install': new_install},

Note: The solution below only works when installing a source distribution zip or tarball, or installing in editable mode from a source tree. It will not work when installing from a binary wheel (.whl)


This is the only strategy that has worked for me when the post-install script requires that the package dependencies have already been installed:

import atexit
from setuptools.command.install import install


def _post_install():
    print('POST INSTALL')


class new_install(install):
    def __init__(self, *args, **kwargs):
        super(new_install, self).__init__(*args, **kwargs)
        atexit.register(_post_install)


setuptools.setup(
    cmdclass={'install': new_install},

回答 2

注意:以下解决方案仅在安装源分发zip或tarball或从源树以可编辑模式安装时有效。从二元轮()安装时将无法使用.whl


一个解决方案可能是post_setup.py在in setup.py目录中包含一个。post_setup.py将包含执行安装后功能的功能,并且setup.py只会在适当的时间导入并启动它。

setup.py

from distutils.core import setup
from distutils.command.install_data import install_data

try:
    from post_setup import main as post_install
except ImportError:
    post_install = lambda: None

class my_install(install_data):
    def run(self):
        install_data.run(self)
        post_install()

if __name__ == '__main__':
    setup(
        ...
        cmdclass={'install_data': my_install},
        ...
    )

post_setup.py

def main():
    """Do here your post-install"""
    pass

if __name__ == '__main__':
    main()

通过setup.py从其目录启动的一般想法,您将能够导入,post_setup.py否则它将启动一个空函数。

在中post_setup.py,该if __name__ == '__main__':语句允许您从命令行手动启动安装后。

Note: The solution below only works when installing a source distribution zip or tarball, or installing in editable mode from a source tree. It will not work when installing from a binary wheel (.whl)


A solution could be to include a post_setup.py in setup.py‘s directory. post_setup.py will contain a function which does the post-install and setup.py will only import and launch it at the appropriate time.

In setup.py:

from distutils.core import setup
from distutils.command.install_data import install_data

try:
    from post_setup import main as post_install
except ImportError:
    post_install = lambda: None

class my_install(install_data):
    def run(self):
        install_data.run(self)
        post_install()

if __name__ == '__main__':
    setup(
        ...
        cmdclass={'install_data': my_install},
        ...
    )

In post_setup.py:

def main():
    """Do here your post-install"""
    pass

if __name__ == '__main__':
    main()

With the common idea of launching setup.py from its directory, you will be able to import post_setup.py else it will launch an empty function.

In post_setup.py, the if __name__ == '__main__': statement allows you to manually launch post-install from command line.


回答 3

结合@ Apalala,@ Zulu和@mertyildiran的答案;这在Python 3.5环境中对我有用:

import atexit
import os
import sys
from setuptools import setup
from setuptools.command.install import install

class CustomInstall(install):
    def run(self):
        def _post_install():
            def find_module_path():
                for p in sys.path:
                    if os.path.isdir(p) and my_name in os.listdir(p):
                        return os.path.join(p, my_name)
            install_path = find_module_path()

            # Add your post install code here

        atexit.register(_post_install)
        install.run(self)

setup(
    cmdclass={'install': CustomInstall},
...

这也使您可以访问中的软件包的安装路径install_path,以进行一些shell工作。

Combining the answers from @Apalala, @Zulu and @mertyildiran; this worked for me in a Python 3.5 environment:

import atexit
import os
import sys
from setuptools import setup
from setuptools.command.install import install

class CustomInstall(install):
    def run(self):
        def _post_install():
            def find_module_path():
                for p in sys.path:
                    if os.path.isdir(p) and my_name in os.listdir(p):
                        return os.path.join(p, my_name)
            install_path = find_module_path()

            # Add your post install code here

        atexit.register(_post_install)
        install.run(self)

setup(
    cmdclass={'install': CustomInstall},
...

This also gives you access the to the installation path of the package in install_path, to do some shell work on.


回答 4

我认为执行后安装并保持要求的最简单方法是装饰对的调用setup(...)

from setup tools import setup


def _post_install(setup):
    def _post_actions():
        do_things()
    _post_actions()
    return setup

setup = _post_install(
    setup(
        name='NAME',
        install_requires=['...
    )
)

这将setup()在声明时运行setup。完成需求安装后,它将运行该_post_install()功能,该功能将运行内部功能_post_actions()

I think the easiest way to perform the post-install, and keep the requirements, is to decorate the call to setup(...):

from setup tools import setup


def _post_install(setup):
    def _post_actions():
        do_things()
    _post_actions()
    return setup

setup = _post_install(
    setup(
        name='NAME',
        install_requires=['...
    )
)

This will run setup() when declaring setup. Once done with the requirements installation, it will run the _post_install() function, which will run the inner function _post_actions().


回答 5

如果使用atexit,则无需创建新的cmdclass。您可以直接在setup()调用之前创建atexit寄存器。它做同样的事情。

另外,如果你需要依赖先安装,但这不是用PIP工作进行安装,因为你的atexit处理程序之前PIP移动套餐到位调用。

If using atexit, there is no need to create a new cmdclass. You can simply create your atexit register right before the setup() call. It does the same thing.

Also, if you need dependencies to be installed first, this does not work with pip install since your atexit handler will be called before pip moves the packages into place.


回答 6

我无法通过提出的任何建议来解决问题,因此这对我有所帮助。

你可以调用功能,你想刚过安装之后运行setup()setup.py,这样的:

from setuptools import setup

def _post_install():
    <your code>

setup(...)

_post_install()

I wasn’t able to solve a problem with any presented recommendations, so here is what helped me.

You can call function, that you want to run after installation just after setup() in setup.py, like that:

from setuptools import setup

def _post_install():
    <your code>

setup(...)

_post_install()

如何编写setup.py以包含Git存储库作为依赖项

问题:如何编写setup.py以包含Git存储库作为依赖项

我正在尝试setup.py为我的包裹写东西。我的软件包需要指定对另一个Git存储库的依赖。

这是我到目前为止所拥有的:

from setuptools import setup, find_packages

setup(
    name='abc',
    packages=find_packages(),
    url='https://github.abc.com/abc/myabc',
    description='This is a description for abc',
    long_description=open('README.md').read(),
    install_requires=[
        "requests==2.7.0",
        "SomePrivateLib>=0.1.0",
        ],
    dependency_links = [
     "git+git://github.abc.com/abc/SomePrivateLib.git#egg=SomePrivateLib",
    ],
    include_package_data=True,
)

当我跑步时:

pip install -e https://github.abc.com/abc/myabc.git#egg=analyse

我懂了

找不到满足SomePrivateLib> = 0.1.0要求的版本(来自分析)(来自版本:)找不到SomePrivateLib> = 0.1.0(来自分析)的匹配分布

我究竟做错了什么?

I am trying to write setup.py for my package. My package needs to specify a dependency on another git repo.

This is what I have so far:

from setuptools import setup, find_packages

setup(
    name='abc',
    packages=find_packages(),
    url='https://github.abc.com/abc/myabc',
    description='This is a description for abc',
    long_description=open('README.md').read(),
    install_requires=[
        "requests==2.7.0",
        "SomePrivateLib>=0.1.0",
        ],
    dependency_links = [
     "git+git://github.abc.com/abc/SomePrivateLib.git#egg=SomePrivateLib",
    ],
    include_package_data=True,
)

When I run:

pip install -e https://github.abc.com/abc/myabc.git#egg=analyse

I get

Could not find a version that satisfies the requirement SomePrivateLib>=0.1.0 (from analyse) (from versions: ) No matching distribution found for SomePrivateLib>=0.1.0 (from analyse)

What am I doing wrong ?


回答 0

您可以在此处找到正确的方法。

dependency_links=['http://github.com/user/repo/tarball/master#egg=package-1.0']

关键不是提供指向Git存储库的链接,而是提供指向tarball的链接。如果您/tarball/master像上面那样添加,GitHub会为您创建master分支的压缩包。

You can find the right way to do it here.

dependency_links=['http://github.com/user/repo/tarball/master#egg=package-1.0']

The key is not to give a link to a git repository, but a link to a tarball. Github creates a tarball of the master branch for you if you append /tarball/master as shown above.


回答 1

在通过上面的评论中@muon链接的pip问题3939PEP-508规范后,我发现成功地通过setup.py使用以下规范模式来安装我的私有repo依赖项install_requires(不再dependency_links):

install_requires = [
  'some-pkg @ git+ssh://git@github.com/someorgname/pkg-repo-name@v1.1#egg=some-pkg',
]

@v1.1指示在GitHub上创建的发布标志,并可以用一个分支,提交,或不同类型的标签来代替。

After digging through the pip issue 3939 linked by @muon in the comments above and then the PEP-508 specification, I found success getting my private repo dependency to install via setup.py using this specification pattern in install_requires (no more dependency_links):

install_requires = [
  'some-pkg @ git+ssh://git@github.com/someorgname/pkg-repo-name@v1.1#egg=some-pkg',
]

The @v1.1 indicates the release tag created on github and could be replaced with a branch, commit, or different type of tag.


回答 2

以下答案不适用于Pip 19+


不幸的是,其他答案不适用于私有存储库,这是最常见的用例之一。我最终的确使用了如下setup.py文件:

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository - needs entry in `dependency_links`
        'ExampleRepo'
    ],

    dependency_links=[
        # Make sure to include the `#egg` portion so the `install_requires` recognizes the package
        'git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

较新版本的pip消除了使用“ dependency_links”的需要,从而使操作更加轻松-

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository
        'ExampleRepo @ git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

The following answer is deprecated for Pip 19+


Unfortunately the other answer does not work with private repositories, which is one of the most common use cases for this. I eventually did get it working with a setup.py file that looks like this:

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository - needs entry in `dependency_links`
        'ExampleRepo'
    ],

    dependency_links=[
        # Make sure to include the `#egg` portion so the `install_requires` recognizes the package
        'git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

Newer versions of pip make this even easier by removing the need to use “dependency_links”-

from setuptools import setup, find_packages

setup(
    name = 'MyProject',
    version = '0.1.0',
    url = '',
    description = '',
    packages = find_packages(),
    install_requires = [
        # Github Private Repository
        'ExampleRepo @ git+ssh://git@github.com/example_organization/ExampleRepo.git#egg=ExampleRepo-0.1'
    ]
)

回答 3

一个更一般的答案:要从requirements.txt文件中获取信息,请执行以下操作:

from setuptools import setup, find_packages
from os import path

loc = path.abspath(path.dirname(__file__))

with open(loc + '/requirements.txt') as f:
    requirements = f.read().splitlines()

required = []
dependency_links = []

# Do not add to required lines pointing to Git repositories
EGG_MARK = '#egg='
for line in requirements:
    if line.startswith('-e git:') or line.startswith('-e git+') or \
            line.startswith('git:') or line.startswith('git+'):
        if EGG_MARK in line:
            package_name = line[line.find(EGG_MARK) + len(EGG_MARK):]
            required.append(package_name)
            dependency_links.append(line)
        else:
            print('Dependency to a git repository should have the format:')
            print('git+ssh://git@github.com/xxxxx/xxxxxx#egg=package_name')
    else:
        required.append(line)

setup(
    name='myproject',  # Required
    version='0.0.1',  # Required
    description='Description here....',  # Required
    packages=find_packages(),  # Required
    install_requires=required,
    dependency_links=dependency_links,
)

A more general answer, to get the information from the requeriments.txt file I do:

from setuptools import setup, find_packages
from os import path

loc = path.abspath(path.dirname(__file__))

with open(loc + '/requirements.txt') as f:
    requirements = f.read().splitlines()

required = []
dependency_links = []
# do not add to required lines pointing to git repositories
EGG_MARK = '#egg='
for line in requirements:
    if line.startswith('-e git:') or line.startswith('-e git+') or \
            line.startswith('git:') or line.startswith('git+'):
        if EGG_MARK in line:
            package_name = line[line.find(EGG_MARK) + len(EGG_MARK):]
            required.append(package_name)
            dependency_links.append(line)
        else:
            print('Dependency to a git repository should have the format:')
            print('git+ssh://git@github.com/xxxxx/xxxxxx#egg=package_name')
    else:
        required.append(line)

setup(
    name='myproject',  # Required
    version='0.0.1',  # Required
    description='Description here....',  # Required
    packages=find_packages(),  # Required
    install_requires=required,
    dependency_links=dependency_links,
) 

回答 4

实际上,如果您希望递归安装软件包(YourCurrentPackage包含SomePrivateLib),例如,当您希望将YourCurrentPackage包含到另一个软件包中(例如OuterPackage→YourCurrentPackage→SomePrivateLib)时,您将同时需要:

install_requires=[
    ...,
    "SomePrivateLib @ git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
],
dependency_links = [
    "git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
]

并确保您使用版本号创建了一个标签。

同样,如果您的Git项目是私有的,并且您想将其安装在容器中(例如DockerGitLab运行器),那么您将需要对存储库的授权访问。请考虑将Git + HTTPS与访问令牌一起使用(例如在GitLab上:https : //docs.gitlab.com/ee/user/profile/personal_access_tokens.html ):

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    ....

    install_requires=[
            ...,
            f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ],
    dependency_links = [
            f"git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

Actually if you like to make your packages installable recursivelly (YourCurrentPackage includes your SomePrivateLib), e.g. when you want to include YourCurrentPackage into another one (like OuterPackage -> YourCurrentPackage -> SomePrivateLib) you’ll need both:

install_requires=[
    ...,
    "SomePrivateLib @ git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
],
dependency_links = [
    "git+ssh://github.abc.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
]

And make sure you have a tag created with your version number.

Also if your git project is private and you like to install it inside the container e.g. Docker or GitLab runner, you will need authorized access to your repo. Please consider to use git+https with access tokens (like on GitLab: https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html):

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    ....

    install_requires=[
            ...,
            f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ],
    dependency_links = [
            f"git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

回答 5

我在GitLab中成功使用了这三个选项。我正在使用GitLab版本11。

选项1-未指定令牌。Shell将提示输入用户名/密码。

from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        "SomePrivateLib @ git+https://gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

选项2-指定了用户访问令牌。通过转到GitLab→帐户右上方→设置→访问令牌生成的令牌。创建具有read_repository权限的令牌。

例:

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

选项3-指定存储库级别的令牌。通过转到存储库→设置→存储库→部署令牌生成的令牌。在这里,创建一个具有read_repository权限的令牌。

例:

import os
from setuptools import setup

TOKEN_USER = os.getenv('EXPORTED_TOKEN_USER')
TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://{TOKEN_USER}:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

在这三者中,我都可以简单地做:“ SomePrivateLib @ git + https://gitlab.server.com/abc/SomePrivateLib.git”,最后没有#egg标记。

I was successful with these 3 options in gitlab. I am using version 11 of gitlab.

option 1 – no token specified. shell will prompt for username/password.

from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        "SomePrivateLib @ git+https://gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

option 2 – user access token specified. token generated by going to gitlab > account top right > settings > access tokens. create token with read_repository rights.

example:

import os
from setuptools import setup

TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://gitlab-ci-token:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

option 3 – repository-level token specified. token generated by going to the repository > settings > repository > deploy tokens. from here create a token with read_repository rights.

example:

import os
from setuptools import setup

TOKEN_USER = os.getenv('EXPORTED_TOKEN_USER')
TOKEN_VALUE = os.getenv('EXPORTED_VAR_WITH_TOKEN')

setup(
    install_requires=[
        f"SomePrivateLib @ git+https://{TOKEN_USER}:{TOKEN_VALUE}@gitlab.server.com/abc/SomePrivateLib.git@0.1.0#egg=SomePrivateLib"
    ]
)

In all 3, I was able to do simply: “SomePrivateLib @ git+https://gitlab.server.com/abc/SomePrivateLib.git” without the #egg marking at the end.


如何获取软件包中setup.py(setuptools)中定义的版本?

问题:如何获取软件包中setup.py(setuptools)中定义的版本?

如何setup.py从软件包中定义版本(出于--version或其他目的)?

How could I get the version defined in setup.py from my package (for --version, or other purposes)?


回答 0

询问已安装发行版的版本字符串

要在运行时从程序包内部检索版本(您的问题实际上是在问什么),可以使用:

import pkg_resources  # part of setuptools
version = pkg_resources.require("MyProject")[0].version

存储版本字符串以在安装期间使用

如果要沿另一方向走动(这似乎是其他答案作者似乎认为您正在询问的问题),请将版本字符串放在单独的文件中,然后在中读取该文件的内容setup.py

您可以在包中用__version__一行创建一个version.py ,然后使用来从setup.py读取它execfile('mypackage/version.py'),以便__version__在setup.py命名空间中进行设置。

如果您想要一种更简单的方法来适用于所有Python版本,甚至可能需要访问版本字符串的非Python语言,请执行以下操作:

将版本字符串存储为纯文本文件(例如)的唯一内容VERSION,并在期间读取该文件setup.py

version_file = open(os.path.join(mypackage_root_dir, 'VERSION'))
version = version_file.read().strip()

这样,同一个VERSION文件在任何其他程序中都可以很好地工作,即使在非Python程序中也是如此,并且您只需要在一个位置更改所有程序的版本字符串即可。

安装过程中有关种族状况的警告

顺便说一句,请不要按照此处另一个答案的建议从setup.py导入软件包:它似乎对您有用(因为您已经安装了软件包的依赖项),但是会对软件包的新用户造成严重破坏,因为如果不先手动安装依赖项,他们将无法安装您的软件包。

Interrogate version string of already-installed distribution

To retrieve the version from inside your package at runtime (what your question appears to actually be asking), you can use:

import pkg_resources  # part of setuptools
version = pkg_resources.require("MyProject")[0].version

Store version string for use during install

If you want to go the other way ’round (which appears to be what other answer authors here appear to have thought you were asking), put the version string in a separate file and read that file’s contents in setup.py.

You could make a version.py in your package with a __version__ line, then read it from setup.py using execfile('mypackage/version.py'), so that it sets __version__ in the setup.py namespace.

If you want a much simpler way that will work with all Python versions and even non-Python languages that may need access to the version string:

Store the version string as the sole contents of a plain text file, named e.g. VERSION, and read that file during setup.py.

version_file = open(os.path.join(mypackage_root_dir, 'VERSION'))
version = version_file.read().strip()

The same VERSION file will then work exactly as well in any other program, even non-Python ones, and you only need to change the version string in one place for all programs.

Warning about race condition during install

By the way, DO NOT import your package from your setup.py as suggested in another answer here: it will seem to work for you (because you already have your package’s dependencies installed), but it will wreak havoc upon new users of your package, as they will not be able to install your package without manually installing the dependencies first.


回答 1

示例研究: mymodule

想象一下这个配置:

setup.py
mymodule/
        / __init__.py
        / version.py
        / myclasses.py

然后,想象一下一些具有依赖项并且setup.py看起来像这样的常见情况:

setup(...
    install_requires=['dep1','dep2', ...]
    ...)

还有一个例子__init__.py

from mymodule.myclasses import *
from mymodule.version import __version__

例如myclasses.py

# these are not installed on your system.
# importing mymodule.myclasses would give ImportError
import dep1
import dep2

问题1:mymodule在安装过程中导入

如果您是setup.py进口公司,mymodule在设置过程中很可能会收到ImportError。当您的软件包具有依赖项时,这是一个非常常见的错误。如果您的程序包除内置程序外没有其他依赖项,则可能是安全的。但是,这不是一个好习惯。这样做的原因是它不是面向未来的。说明天您的代码需要使用其他依赖项。

问题2:我的位置在哪里__version__

如果您进行硬编码__version__setup.py则它可能与您模块中附带的版本不匹配。为了保持一致,您可以将其放在一个地方,并在需要时从同一地方阅读。使用import您可能会遇到问题1。

解决方案:àla setuptools

您可以结合使用openexec并提供dict exec来添加变量:

# setup.py
from setuptools import setup, find_packages
from distutils.util import convert_path

main_ns = {}
ver_path = convert_path('mymodule/version.py')
with open(ver_path) as ver_file:
    exec(ver_file.read(), main_ns)

setup(...,
    version=main_ns['__version__'],
    ...)

mymodule/version.py公开版本:

__version__ = 'some.semantic.version'

这样,该版本将随模块一起提供,并且在安装期间尝试导入缺少依赖项(尚未安装)的模块时不会出现问题。

example study: mymodule

Imagine this configuration:

setup.py
mymodule/
        / __init__.py
        / version.py
        / myclasses.py

Then imagine some usual scenario where you have dependencies and setup.py looks like:

setup(...
    install_requires=['dep1','dep2', ...]
    ...)

And an example __init__.py:

from mymodule.myclasses import *
from mymodule.version import __version__

And for example myclasses.py:

# these are not installed on your system.
# importing mymodule.myclasses would give ImportError
import dep1
import dep2

problem #1: importing mymodule during setup

If your setup.py imports mymodule then during setup you would most likely get an ImportError. This is a very common error when your package has dependencies. If your package does not have other dependencies than the builtins, you may be safe; however this isn’t a good practice. The reason for that is that it is not future-proof; say tomorrow your code needs to consume some other dependency.

problem #2: where’s my __version__ ?

If you hardcode __version__ in setup.py then it may not match the version that you would ship in your module. To be consistent, you would put it in one place and read it from the same place when you need it. Using import you may get the problem #1.

solution: à la setuptools

You would use a combination of open, exec and provide a dict for exec to add variables:

# setup.py
from setuptools import setup, find_packages
from distutils.util import convert_path

main_ns = {}
ver_path = convert_path('mymodule/version.py')
with open(ver_path) as ver_file:
    exec(ver_file.read(), main_ns)

setup(...,
    version=main_ns['__version__'],
    ...)

And in mymodule/version.py expose the version:

__version__ = 'some.semantic.version'

This way, the version is shipped with the module, and you do not have issues during setup trying to import a module that has missing dependencies (yet to be installed).


回答 2

最好的技术是__version__在产品代码中定义,然后从那里将其导入setup.py。这为您提供了一个值,您可以在正在运行的模块中读取该值,并且只有一个地方可以对其进行定义。

setup.py中的值未安装,并且setup.py在安装后不会停留。

我在coverage.py中所做的(例如):

# coverage/__init__.py
__version__ = "3.2"


# setup.py
from coverage import __version__

setup(
    name = 'coverage',
    version = __version__,
    ...
    )

更新(2017):coverage.py不再自行导入以获取版本。导入自己的代码可以使其可卸载,因为您的产品代码将尝试导入尚未安装的依赖项,因为setup.py是安装它们的原因。

The best technique is to define __version__ in your product code, then import it into setup.py from there. This gives you a value you can read in your running module, and have only one place to define it.

The values in setup.py are not installed, and setup.py doesn’t stick around after installation.

What I did (for example) in coverage.py:

# coverage/__init__.py
__version__ = "3.2"


# setup.py
from coverage import __version__

setup(
    name = 'coverage',
    version = __version__,
    ...
    )

UPDATE (2017): coverage.py no longer imports itself to get the version. Importing your own code can make it uninstallable, because you product code will try to import dependencies, which aren’t installed yet, because setup.py is what installs them.


回答 3

您的问题有点模糊,但是我想您要问的是如何指定它。

您需要这样定义__version__

__version__ = '1.4.4'

然后,您可以确认setup.py知道您刚刚指定的版本:

% ./setup.py --version
1.4.4

Your question is a little vague, but I think what you are asking is how to specify it.

You need to define __version__ like so:

__version__ = '1.4.4'

And then you can confirm that setup.py knows about the version you just specified:

% ./setup.py --version
1.4.4

回答 4

我对这些答案不满意…不想使用setuptools,也不想为单个变量制作整个单独的模块,所以我想到了这些。

当您确定主模块为pep8样式并将保持这种状态时:

version = '0.30.unknown'
with file('mypkg/mymod.py') as f:
    for line in f:
        if line.startswith('__version__'):
            _, _, version = line.replace("'", '').split()
            break

如果您要格外小心并使用真实的解析器:

import ast
version = '0.30.unknown2'
with file('mypkg/mymod.py') as f:
    for line in f:
        if line.startswith('__version__'):
            version = ast.parse(line).body[0].value.s
            break

setup.py在某种程度上是一个一次性模块,因此如果它有点难看,则不是问题。


更新:有趣的是,近年来,我已经摆脱了这一点,开始使用名为的软件包中的单独文件meta.py。我在其中放置了很多可能需要经常更改的元数据。因此,不仅仅是为了一个价值。

I wasn’t happy with these answers… didn’t want to require setuptools, nor make a whole separate module for a single variable, so I came up with these.

For when you are sure the main module is in pep8 style and will stay that way:

version = '0.30.unknown'
with file('mypkg/mymod.py') as f:
    for line in f:
        if line.startswith('__version__'):
            _, _, version = line.replace("'", '').split()
            break

If you’d like to be extra careful and use a real parser:

import ast
version = '0.30.unknown2'
with file('mypkg/mymod.py') as f:
    for line in f:
        if line.startswith('__version__'):
            version = ast.parse(line).body[0].value.s
            break

setup.py is somewhat of a throwaway module so not an issue if it is a bit ugly.


Update: funny enough I’ve moved away from this in recent years and started using a separate file in the package called meta.py. I put lots of meta data in there that I might want to change frequently. So, not just for one value.


回答 5

在源代码树中创建文件,例如在yourbasedir / yourpackage / _version.py中。让该文件仅包含一行代码,如下所示:

__version__ = "1.1.0-r4704"

然后在setup.py中,打开该文件并解析出如下版本号:

verstr =“未知”
尝试:
    verstrline = open('yourpackage / _version.py',“ rt”)。read()
除了EnvironmentError:
    通过#好的,没有版本文件。
其他:
    VSRE = r“ ^ __ version__ = ['\”]([^ \\]] *)['\“]”
    mo = re.search(VSRE,verstrline,re.M)
    如果莫:
        verstr = mo.group(1)
    其他:
        引发RuntimeError(“无法在您的package / _version.py中找到版本”)

最后,在yourbasedir/yourpackage/__init__.pyimport _version中,如下所示:

__version__ =“未知”
尝试:
    从_version导入__version__
除了ImportError:
    #我们正在没有_version.py的树中运行,因此我们不知道我们的版本是什么。
    通过

我维护的“ pyutil”包就是一个执行此操作的代码示例。(请参阅PyPI或Google搜索-stackoverflow不允许我在此答案中包含指向它的超链接。)

@pjeby是正确的,您不应从其自己的setup.py导入软件包。当您通过创建一个新的Python解释器并在其中首先执行setup.py对其python setup.py进行测试时,它将起作用,但是在某些情况下它将无法工作。那是因为import youpackage这并不意味着要读取名为“ yourpackage”的目录的当前工作目录,而是要在当前目录中查找sys.modules键“ yourpackage”,然后在不存在的情况下执行各种操作。所以它总是在工作时起作用,python setup.py因为您有一个空的sys.modules,但这通常并不起作用。

例如,如果py2exe在打包应用程序的过程中正在执行setup.py怎么办?我见过这样的情况,其中py2exe会在软件包上放置错误的版本号,因为该软件包正在从中获取其版本号import myownthing在其setup.py中,但是以前在py2exe运行期间导入了该软件包的其他版本。同样,如果setuptools,easy_install,distribution或distutils2尝试在安装依赖于您的软件包的其他软件包的过程中尝试构建软件包,该怎么办?然后,在评估setup.py时是否可以导入您的软件包,或者在此Python解释器的生命周期中是否已经导入了您的软件包的版本,或者导入您的软件包是否需要先安装其他软件包? ,或有副作用,可以改变结果。我在尝试重新使用Python软件包时遇到了很多困难,这导致py2exe和setuptools之类的工具出现问题,因为它们的setup.py会导入软件包本身以查找其版本号。

顺便说一句,这种技术可以很好地与自动yourpackage/_version.py为您创建文件的工具配合使用,例如,通过读取修订控制历史记录并根据修订控制历史记录中的最新标记写出版本号来实现。这是一个针对darcs的工具:http : //tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst,这是一个对git进行相同操作的代码片段:http:// github .com / warner / python-ecdsa / blob / 0ed702a9d4057ecf33eea969b8cf280eaccd89a1 / setup.py#L34

Create a file in your source tree, e.g. in yourbasedir/yourpackage/_version.py . Let that file contain only a single line of code, like this:

__version__ = "1.1.0-r4704"

Then in your setup.py, open that file and parse out the version number like this:

verstr = "unknown"
try:
    verstrline = open('yourpackage/_version.py', "rt").read()
except EnvironmentError:
    pass # Okay, there is no version file.
else:
    VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
    mo = re.search(VSRE, verstrline, re.M)
    if mo:
        verstr = mo.group(1)
    else:
        raise RuntimeError("unable to find version in yourpackage/_version.py")

Finally, in yourbasedir/yourpackage/__init__.py import _version like this:

__version__ = "unknown"
try:
    from _version import __version__
except ImportError:
    # We're running in a tree that doesn't have a _version.py, so we don't know what our version is.
    pass

An example of code that does this is the “pyutil” package that I maintain. (See PyPI or google search — stackoverflow is disallowing me from including a hyperlink to it in this answer.)

@pjeby is right that you shouldn’t import your package from its own setup.py. That will work when you test it by creating a new Python interpreter and executing setup.py in it first thing: python setup.py, but there are cases when it won’t work. That’s because import youpackage doesn’t mean to read the current working directory for a directory named “yourpackage”, it means to look in the current sys.modules for a key “yourpackage” and then to do various things if it isn’t there. So it always works when you do python setup.py because you have a fresh, empty sys.modules, but this doesn’t work in general.

For example, what if py2exe is executing your setup.py as part of the process of packaging up an application? I’ve seen a case like this where py2exe would put the wrong version number on a package because the package was getting its version number from import myownthing in its setup.py, but a different version of that package had previously been imported during the py2exe run. Likewise, what if setuptools, easy_install, distribute, or distutils2 is trying to build your package as part of a process of installing a different package that depends on yours? Then whether your package is importable at the time that its setup.py is being evaluated, or whether there is already a version of your package that has been imported during this Python interpreter’s life, or whether importing your package requires other packages to be installed first, or has side-effects, can change the results. I’ve had several struggles with trying to re-use Python packages which caused problems for tools like py2exe and setuptools because their setup.py imports the package itself in order to find its version number.

By the way, this technique plays nicely with tools to automatically create the yourpackage/_version.py file for you, for example by reading your revision control history and writing out a version number based on the most recent tag in revision control history. Here is a tool that does that for darcs: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst and here is a code snippet which does the same thing for git: http://github.com/warner/python-ecdsa/blob/0ed702a9d4057ecf33eea969b8cf280eaccd89a1/setup.py#L34


回答 6

使用正则表达式并根据元数据字段具有如下格式,这也应该起作用:

__fieldname__ = 'value'

在setup.py的开头使用以下命令:

import re
main_py = open('yourmodule.py').read()
metadata = dict(re.findall("__([a-z]+)__ = '([^']+)'", main_py))

之后,您可以像下面这样在脚本中使用元数据:

print 'Author is:', metadata['author']
print 'Version is:', metadata['version']

This should also work, using regular expressions and depending on the metadata fields to have a format like this:

__fieldname__ = 'value'

Use the following at the beginning of your setup.py:

import re
main_py = open('yourmodule.py').read()
metadata = dict(re.findall("__([a-z]+)__ = '([^']+)'", main_py))

After that, you can use the metadata in your script like this:

print 'Author is:', metadata['author']
print 'Version is:', metadata['version']

回答 7

具有这样的结构:

setup.py
mymodule/
        / __init__.py
        / version.py
        / myclasses.py

其中version.py包含:

__version__ = 'version_string'

您可以在setup.py中执行此操作:

import sys

sys.path[0:0] = ['mymodule']

from version import __version__

这不会对mymodule / __ init__.py中的任何依赖项造成任何问题

With a structure like this:

setup.py
mymodule/
        / __init__.py
        / version.py
        / myclasses.py

where version.py contains:

__version__ = 'version_string'

You can do this in setup.py:

import sys

sys.path[0:0] = ['mymodule']

from version import __version__

This won’t cause any problem with whatever dependencies you have in your mymodule/__init__.py


回答 8

为了避免导入文件(从而执行其代码),可以解析文件并version从语法树中恢复属性:

# assuming 'path' holds the path to the file

import ast

with open(path, 'rU') as file:
    t = compile(file.read(), path, 'exec', ast.PyCF_ONLY_AST)
    for node in (n for n in t.body if isinstance(n, ast.Assign)):
        if len(node.targets) == 1:
            name = node.targets[0]
            if isinstance(name, ast.Name) and \
                    name.id in ('__version__', '__version_info__', 'VERSION'):
                v = node.value
                if isinstance(v, ast.Str):
                    version = v.s
                    break
                if isinstance(v, ast.Tuple):
                    r = []
                    for e in v.elts:
                        if isinstance(e, ast.Str):
                            r.append(e.s)
                        elif isinstance(e, ast.Num):
                            r.append(str(e.n))
                    version = '.'.join(r)
                    break

这段代码试图在模块的顶层查找__version__VERSION返回字符串值。右侧可以是字符串或元组。

To avoid importing a file (and thus executing its code) one could parse it and recover the version attribute from the syntax tree:

# assuming 'path' holds the path to the file

import ast

with open(path, 'rU') as file:
    t = compile(file.read(), path, 'exec', ast.PyCF_ONLY_AST)
    for node in (n for n in t.body if isinstance(n, ast.Assign)):
        if len(node.targets) == 1:
            name = node.targets[0]
            if isinstance(name, ast.Name) and \
                    name.id in ('__version__', '__version_info__', 'VERSION'):
                v = node.value
                if isinstance(v, ast.Str):
                    version = v.s
                    break
                if isinstance(v, ast.Tuple):
                    r = []
                    for e in v.elts:
                        if isinstance(e, ast.Str):
                            r.append(e.s)
                        elif isinstance(e, ast.Num):
                            r.append(str(e.n))
                    version = '.'.join(r)
                    break

This code tries to find the __version__ or VERSION assignment at the top level of the module return is string value. The right side can be either a string or a tuple.


回答 9

有上千种为猫皮的方法-这是我的:

# Copied from (and hacked):
# https://github.com/pypa/virtualenv/blob/develop/setup.py#L42
def get_version(filename):
    import os
    import re

    here = os.path.dirname(os.path.abspath(__file__))
    f = open(os.path.join(here, filename))
    version_file = f.read()
    f.close()
    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.")

There’s a thousand ways to skin a cat — here’s mine:

# Copied from (and hacked):
# https://github.com/pypa/virtualenv/blob/develop/setup.py#L42
def get_version(filename):
    import os
    import re

    here = os.path.dirname(os.path.abspath(__file__))
    f = open(os.path.join(here, filename))
    version_file = f.read()
    f.close()
    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.")

回答 10

从@ gringo-suave 清理https://stackoverflow.com/a/12413800

from itertools import ifilter
from os import path
from ast import parse

with open(path.join('package_name', '__init__.py')) as f:
    __version__ = parse(next(ifilter(lambda line: line.startswith('__version__'),
                                     f))).body[0].value.s

Cleaning up https://stackoverflow.com/a/12413800 from @gringo-suave:

from itertools import ifilter
from os import path
from ast import parse

with open(path.join('package_name', '__init__.py')) as f:
    __version__ = parse(next(ifilter(lambda line: line.startswith('__version__'),
                                     f))).body[0].value.s

回答 11

现在这很麻烦,需要进行一些改进(我可能错过了pkg_resources中甚至可能有一个未发现的成员调用),但是我根本看不出为什么这行不通,也为什么至今没有人建议这样做(在Google周围搜索没有出现)…请注意,这是Python 2.x,需要pkg_resources(叹气):

import pkg_resources

version_string = None
try:
    if pkg_resources.working_set is not None:
        disto_obj = pkg_resources.working_set.by_key.get('<my pkg name>', None)
        # (I like adding ", None" to gets)
        if disto_obj is not None:
            version_string = disto_obj.version
except Exception:
    # Do something
    pass

Now this is gross and needs some refining (there may even be an uncovered member call in pkg_resources that I missed), but I simply do not see why this doesn’t work, nor why no one has suggested it to date (Googling around has not turned this up)…note that this is Python 2.x, and would require requiring pkg_resources (sigh):

import pkg_resources

version_string = None
try:
    if pkg_resources.working_set is not None:
        disto_obj = pkg_resources.working_set.by_key.get('<my pkg name>', None)
        # (I like adding ", None" to gets)
        if disto_obj is not None:
            version_string = disto_obj.version
except Exception:
    # Do something
    pass

回答 12

我们希望将有关我们程序包的元信息放入pypackagery__init__.py,但由于PJ Eby已经指出(请参阅他的回答和有关比赛条件的警告),它具有第三方依赖性,因此无法这样做。

我们通过创建一个pypackagery_meta.py包含元信息的单独模块来解决该问题:

"""Define meta information about pypackagery package."""

__title__ = 'pypackagery'
__description__ = ('Package a subset of a monorepo and '
                   'determine the dependent packages.')
__url__ = 'https://github.com/Parquery/pypackagery'
__version__ = '1.0.0'
__author__ = 'Marko Ristin'
__author_email__ = 'marko.ristin@gmail.com'
__license__ = 'MIT'
__copyright__ = 'Copyright 2018 Parquery AG'

然后将元信息导入packagery/__init__.py

# ...

from pypackagery_meta import __title__, __description__, __url__, \
    __version__, __author__, __author_email__, \
    __license__, __copyright__

# ...

并最终用于setup.py

import pypackagery_meta

setup(
    name=pypackagery_meta.__title__,
    version=pypackagery_meta.__version__,
    description=pypackagery_meta.__description__,
    long_description=long_description,
    url=pypackagery_meta.__url__,
    author=pypackagery_meta.__author__,
    author_email=pypackagery_meta.__author_email__,
    # ...
    py_modules=['packagery', 'pypackagery_meta'],
 )

您必须pypackagery_meta使用py_modulessetup参数将其包含在包中。否则,您将无法在安装时导入它,因为打包的发行版将缺少它。

We wanted to put the meta information about our package pypackagery in __init__.py, but could not since it has third-party dependencies as PJ Eby already pointed out (see his answer and the warning regarding the race condition).

We solved it by creating a separate module pypackagery_meta.py that contains only the meta information:

"""Define meta information about pypackagery package."""

__title__ = 'pypackagery'
__description__ = ('Package a subset of a monorepo and '
                   'determine the dependent packages.')
__url__ = 'https://github.com/Parquery/pypackagery'
__version__ = '1.0.0'
__author__ = 'Marko Ristin'
__author_email__ = 'marko.ristin@gmail.com'
__license__ = 'MIT'
__copyright__ = 'Copyright 2018 Parquery AG'

then imported the meta information in packagery/__init__.py:

# ...

from pypackagery_meta import __title__, __description__, __url__, \
    __version__, __author__, __author_email__, \
    __license__, __copyright__

# ...

and finally used it in setup.py:

import pypackagery_meta

setup(
    name=pypackagery_meta.__title__,
    version=pypackagery_meta.__version__,
    description=pypackagery_meta.__description__,
    long_description=long_description,
    url=pypackagery_meta.__url__,
    author=pypackagery_meta.__author__,
    author_email=pypackagery_meta.__author_email__,
    # ...
    py_modules=['packagery', 'pypackagery_meta'],
 )

You must include pypackagery_meta into your package with py_modules setup argument. Otherwise, you can not import it upon installation since the packaged distribution would lack it.


回答 13

简单明了,创建一个source/package_name/version.py包含以下内容的文件:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
__version__ = "2.6.9"

然后,在文件上source/package_name/__init__.py,导入供其他人使用的版本:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from .version import __version__

现在,您可以将其放在 setup.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys

try:
    filepath = 'source/package_name/version.py'
    version_file = open( filepath )
    __version__ ,= re.findall( '__version__ = "(.*)"', version_file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

finally:
    version_file.close()

与Python测试了这个2.73.33.43.53.63.7在Linux,Windows和Mac OS。我在包装上使用了针对所有这些平台的集成和单元测试。您可以从.travis.ymlappveyor.yml在此处查看结果:

  1. https://travis-ci.org/evandrocoan/debugtools/builds/527110800
  2. https://ci.appveyor.com/project/evandrocoan/pythondebugtools/builds/24245446

备用版本使用上下文管理器:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys

try:
    filepath = 'source/package_name/version.py'

    with open( filepath ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

您还可以使用该codecs模块在Python 2.73.6

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys
import codecs

try:
    filepath = 'source/package_name/version.py'

    with codecs.open( filepath, 'r', errors='ignore' ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

如果要使用Python C扩展在C / C ++中100%编写Python模块,则可以执行相同的操作,但要使用C / C ++而不是Python。

在这种情况下,创建以下内容setup.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys
import codecs
from setuptools import setup, Extension

try:
    filepath = 'source/version.h'

    with codecs.open( filepath, 'r', errors='ignore' ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

setup(
        name = 'package_name',
        version = __version__,

        package_data = {
                '': [ '**.txt', '**.md', '**.py', '**.h', '**.hpp', '**.c', '**.cpp' ],
            },

        ext_modules = [
            Extension(
                name = 'package_name',
                sources = [
                    'source/file.cpp',
                ],
                include_dirs = ['source'],
            )
        ],
    )

从文件中读取版本version.h

const char* __version__ = "1.0.12";

但是,不要忘记创建MANIFEST.in包含version.h文件:

include README.md
include LICENSE.txt

recursive-include source *.h

并通过以下方式集成到主要应用程序中:

#include <Python.h>
#include "version.h"

// create the module
PyMODINIT_FUNC PyInit_package_name(void)
{
    PyObject* thismodule;
    ...

    // https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue
    PyObject_SetAttrString( thismodule, "__version__", Py_BuildValue( "s", __version__ ) );

    ...
}

参考文献:

  1. python打开文件错误
  2. 通过C API在Python模块中定义全局变量
  3. 如何在setuptools / distribute中包含软件包数据?
  4. https://github.com/lark-parser/lark/blob/master/setup.py#L4
  5. 如何使用具有相同名称的setuptools软件包和ext_modules?
  6. 是否可以使用dist utils(setup.py)作为包数据的一部分包含子目录?

Simple and straight, create a file called source/package_name/version.py with the following contents:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
__version__ = "2.6.9"

Then, on your file source/package_name/__init__.py, you import the version for other people to use:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from .version import __version__

Now, you can put this on setup.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys

try:
    filepath = 'source/package_name/version.py'
    version_file = open( filepath )
    __version__ ,= re.findall( '__version__ = "(.*)"', version_file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

finally:
    version_file.close()

Tested this with Python 2.7, 3.3, 3.4, 3.5, 3.6 and 3.7 on Linux, Windows and Mac OS. I used on my package which has Integration and Unit Tests for all theses platforms. You can see the results from .travis.yml and appveyor.yml here:

  1. https://travis-ci.org/evandrocoan/debugtools/builds/527110800
  2. https://ci.appveyor.com/project/evandrocoan/pythondebugtools/builds/24245446

An alternate version is using context manager:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys

try:
    filepath = 'source/package_name/version.py'

    with open( filepath ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

You can also be using the codecs module to handle unicode errors both on Python 2.7 and 3.6

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys
import codecs

try:
    filepath = 'source/package_name/version.py'

    with codecs.open( filepath, 'r', errors='ignore' ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

If you are writing a Python module 100% in C/C++ using Python C Extensions, you can do the same thing, but using C/C++ instead of Python.

On this case, create the following setup.py:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys
import codecs
from setuptools import setup, Extension

try:
    filepath = 'source/version.h'

    with codecs.open( filepath, 'r', errors='ignore' ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

setup(
        name = 'package_name',
        version = __version__,

        package_data = {
                '': [ '**.txt', '**.md', '**.py', '**.h', '**.hpp', '**.c', '**.cpp' ],
            },

        ext_modules = [
            Extension(
                name = 'package_name',
                sources = [
                    'source/file.cpp',
                ],
                include_dirs = ['source'],
            )
        ],
    )

Which reads the version from the file version.h:

const char* __version__ = "1.0.12";

But, do not forget to create the MANIFEST.in to include the version.h file:

include README.md
include LICENSE.txt

recursive-include source *.h

And it is integrated into the main application with:

#include <Python.h>
#include "version.h"

// create the module
PyMODINIT_FUNC PyInit_package_name(void)
{
    PyObject* thismodule;
    ...

    // https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue
    PyObject_SetAttrString( thismodule, "__version__", Py_BuildValue( "s", __version__ ) );

    ...
}

References:

  1. python open file error
  2. Define a global in a Python module from a C API
  3. How to include package data with setuptools/distribute?
  4. https://github.com/lark-parser/lark/blob/master/setup.py#L4
  5. How to use setuptools packages and ext_modules with the same name?
  6. Is it possible to include subdirectories using dist utils (setup.py) as part of package data?

回答 14

将包部署到服务器和索引包的文件命名约定:

pip动态版本转换的示例:

  • 赢得:

    • test_pkg-1.0.0-cp36-cp36m-win_amd64.whl
    • test_pkg-1.0.0-py3.6-win-amd64.egg
  • 苹果电脑:

    • test_pkg-1.0.0-py3.7-macosx-10.12-x86_64.egg
    • test_pkg-1.0.0-py3.7-macosx-10.12-x86_64.whl
  • Linux:
    • test_pkg-1.0.0-cp36-cp36m-linux_x86_64.whl
from setuptools_scm import get_version

def _get_version():

     dev_version = str(".".join(map(str, str(get_version()).split("+")[0]\
            .split('.')[:-1])))

    return dev_version

找到示例setup.py从git commit调用匹配的动态pip版本

setup(
    version=_get_version(),
    name=NAME,
    description=DESCRIPTION,
    long_description=LONG_DESCRIPTION,
    classifiers=CLASSIFIERS,

# add few more for wheel wheel package ...conversion

)

deploy package to server and file naming convention for indices packages :

example for pip dynamic version conversion:

  • win:

    • test_pkg-1.0.0-cp36-cp36m-win_amd64.whl
    • test_pkg-1.0.0-py3.6-win-amd64.egg
  • mac:

    • test_pkg-1.0.0-py3.7-macosx-10.12-x86_64.egg
    • test_pkg-1.0.0-py3.7-macosx-10.12-x86_64.whl
  • linux:
    • test_pkg-1.0.0-cp36-cp36m-linux_x86_64.whl
from setuptools_scm import get_version

def _get_version():

     dev_version = str(".".join(map(str, str(get_version()).split("+")[0]\
            .split('.')[:-1])))

    return dev_version

Find the sample setup.py calls the dynamic pip version matching from git commit

setup(
    version=_get_version(),
    name=NAME,
    description=DESCRIPTION,
    long_description=LONG_DESCRIPTION,
    classifiers=CLASSIFIERS,

# add few more for wheel wheel package ...conversion

)

回答 15

我正在使用以下环境变量

VERSION = 0.0.0 python setup.py sdist bdist_wheel

在setup.py中


import os

setup(
    version=os.environ['VERSION'],
    ...
)

为了与打包程序版本进行一致性检查,我使用以下脚本。

PKG_VERSION=`python -c "import pkg; print(pkg.__version__)"`
if [ $PKG_VERSION == $VERSION ]; then
    python setup.py sdist bdist_wheel
else
    echo "Package version differs from set env variable"
fi

I am using an environment variable as below

VERSION=0.0.0 python setup.py sdist bdist_wheel

In setup.py


import os

setup(
    version=os.environ['VERSION'],
    ...
)

For consistency check with packer version, I am using below script.

PKG_VERSION=`python -c "import pkg; print(pkg.__version__)"`
if [ $PKG_VERSION == $VERSION ]; then
    python setup.py sdist bdist_wheel
else
    echo "Package version differs from set env variable"
fi

我如何让setuptools安装不在PyPI上的软件包?

问题:我如何让setuptools安装不在PyPI上的软件包?

我刚刚开始使用setuptools和virtualenv。我的软件包需要最新的python-gearman,该工具仅可从GitHub获得。PyPI上的python-gearman版本是一个旧版本。Github源代码与setuptools兼容,即具有setup.py等。是否可以通过一种方法来使setuptools下载并安装新版本,而不是在PyPI上寻找并安装旧版本?

仅供参考,新的python-gearman是http://github.com/mtai/python-gearman

I’ve just started working with setuptools and virtualenv. My package requires the latest python-gearman that is only available from GitHub. The python-gearman version that’s on PyPI is an old one. The Github source is setuptools-compatible, i.e. has setup.py, etc. Is there a way to make setuptools download and install the new version instead of looking for it on PyPI and installing the old one?

FYI, the new python-gearman is http://github.com/mtai/python-gearman


回答 0

关键是告诉easy_install软件包可以在哪里下载。在这种情况下,可以在url http://github.com/mtai/python-gearman/tarball/master找到。但是,该链接本身不起作用,因为easy_install不能仅通过查看URL来知道它将会得到什么。

通过将其更改为http://github.com/mtai/python-gearman/tarball/master#egg=gearman-2.0.0beta,easy_install将能够识别软件包名称及其版本。

最后一步是将URL添加到包的dependency_links中,例如:

setup(
   ...
   dependency_links = ['http://github.com/mtai/python-gearman/tarball/master#egg=gearman-2.0.0beta']
)

现在,在安装您的软件包时,easy_install将发现有一个可从该URL下载的“ gearman 2.0.0beta”,如果您指定“ gearman> = 2.0.0beta”,则可以在PyPI上愉快地选择它。在你的依赖中..

(通常,完成此类操作的方法是在一个人的PyPI页面上包含指向可下载源的链接;在这种情况下,如果gearman软件包的作者已包含上述链接,则您已经设置好了通常,人们用’myproject-dev’标记开发版本,然后人们使用’myproject> = somever,== dev’的要求,因此,如果没有更高版本的软件包,easy_install将尝试查看或下载该版本。)

使用--process-dependency-links时需要指定pip。请注意,不赞成使用依赖项链接处理,在以后的版本中将删除它。

The key is to tell easy_install where the package can be downloaded. In this particular case, it can be found at the url http://github.com/mtai/python-gearman/tarball/master. However, that link by itself won’t work, because easy_install can’t tell just by looking at the URL what it’s going to get.

By changing it to http://github.com/mtai/python-gearman/tarball/master#egg=gearman-2.0.0beta instead, easy_install will be able to identify the package name and its version.

The final step is to add the URL to your package’s dependency_links, e.g.:

setup(
   ...
   dependency_links = ['http://github.com/mtai/python-gearman/tarball/master#egg=gearman-2.0.0beta']
)

Now, when YOUR package is being installed, easy_install will discover that there is a “gearman 2.0.0beta” available for download from that URL, and happily pick it over the one on PyPI, if you specify “gearman>=2.0.0beta” in your dependencies..

(Normally, the way this sort of thing is done is to include a link on one’s PyPI page to the downloadable source; in this case, if the author of the gearman package had included a link like the above, you’d be already set. Typically, people mark the development version with ‘myproject-dev’ and then people use a requirement of ‘myproject>=somever,==dev’, so that if there isn’t a package of somever or higher, easy_install will try to check out or download the release.)

You’ll need to specify --process-dependency-links when using pip. Note that dependency links processing has been deprecated and will be removed in a future release.


回答 1

您可以使用该pip install protocol+location[@tag][#egg=Dependency]格式通过pip直接从源安装。

吉特

pip install git+https://github.com/username/repo.git
pip install git+https://github.com/username/repo.git@MyTag
pip install git+https://github.com/username/repo.git@MyTag#egg=ProjectName

水银

pip install hg+https://hg.myproject.org/MyProject/

SVN

pip install svn+svn://svn.myproject.org/svn/MyProject

z

pip install bzr+http://bzr.myproject.org/MyProject/trunk

支持以下协议: [+git, +svn, +hg, +bzr]

版本号

@tag 可让您指定要检出的特定版本/标签。

#egg=name 使您可以指定项目作为其他项目的依赖项。

订单必须始终为@tag#egg=name

私人仓库

您还可以通过将协议更改为SSH(ssh://)并添加适当的用户(git@)从专用存储库进行安装:

git+ssh://git@github.com/username/my_private_repo

您也可以使用用户名/密码从私人存储库安装。

git+https://<username>:<password>@github.com/<user>/<repo>.git

Github提供了创建可循环使用的个人OAuth令牌的功能

git+https://<oauth token>:x-oauth-basic@github.com/<user>/<repo>.git

requirements.txt

requirements.txt 用于指定项目依赖项:

requirements.txt

package1
package2==1.0.2
package3>=0.0.4
git+https://github.com/username/repo.git

这些不是随软件包一起自动安装的,必须通过命令安装pip -r requirements.txt

包括需求文件

需求文件可以包括其他需求文件:

requirements-docs.txt

sphinx
-r requirements-dev.txt

requirements-dev.txt

some-dev-tool
-r requirements.txt

requirements.txt

package1
package2==1.0.2
package3>=0.0.4
git+https://github.com/username/repo.git

setup.py

需求文件可以安装setup.py使用以下命令指定的依赖项:

-e .

setup.py也可以使用与上述相同的语法从存储库进行安装,但使用此答案中dependency_links提到的值。

参考文献:

https://pip.pypa.io/zh_CN/latest/user_guide.html#installing-packages https://pip.pypa.io/zh-CN/latest/reference/pip_install.html

You can use the pip install protocol+location[@tag][#egg=Dependency] format to install directly from source using pip.

Git

pip install git+https://github.com/username/repo.git
pip install git+https://github.com/username/repo.git@MyTag
pip install git+https://github.com/username/repo.git@MyTag#egg=ProjectName

Mercurial

pip install hg+https://hg.myproject.org/MyProject/

SVN

pip install svn+svn://svn.myproject.org/svn/MyProject

Bzr

pip install bzr+http://bzr.myproject.org/MyProject/trunk

The following protocols are supported: [+git, +svn, +hg, +bzr]

Versions

@tag lets you specify a specific version/tag to check out.

#egg=name lets you specify what the project is as a dependency for others.

The order must always be @tag#egg=name.

Private Repositories

You can also install from private repositories by changing the protocol to SSH (ssh://) and adding an appropriate user (git@):

git+ssh://git@github.com/username/my_private_repo

You can also install from private repositories with a username / password.

git+https://<username>:<password>@github.com/<user>/<repo>.git

Github provides the ability to create personal OAuth tokens which can be cycled

git+https://<oauth token>:x-oauth-basic@github.com/<user>/<repo>.git

requirements.txt

requirements.txt is used to specify project dependencies:

requirements.txt

package1
package2==1.0.2
package3>=0.0.4
git+https://github.com/username/repo.git

These are not installed automatically with the package and must be installed with the command pip -r requirements.txt.

Including requirements files

Requirements files can include other requirements files:

requirements-docs.txt

sphinx
-r requirements-dev.txt

requirements-dev.txt

some-dev-tool
-r requirements.txt

requirements.txt

package1
package2==1.0.2
package3>=0.0.4
git+https://github.com/username/repo.git

setup.py

Requirements files can install dependencies specified in setup.py with the following command:

-e .

setup.py can also install from repositories using the same syntax as above, but using the dependency_links value as mentioned in this answer.

References:

https://pip.pypa.io/en/latest/user_guide.html#installing-packages https://pip.pypa.io/en/latest/reference/pip_install.html


回答 2

正如我刚刚做同样的事情,我发现了另一种方式来做到这一点作为pip--process-dependency-links计划在被删除pip按照19.0 此评论

pip 18.1包含以下功能

允许将PEP 508 URL要求用作依赖项。

PEP 508 的描述中,此类URL依赖项的语法如下:

基于URL的最小查找:

pip @ https://github.com/pypa/pip/archive/1.3.1.zip#sha1=da9234ee9982d4bbb3c72346a6de940a148ea686

所以在你setup.py看来

setup(
   ...
   install_requires = [
   ...
   'python-gearman @ https://github.com/mtai/python-gearman/archive/master.zip'
   ...
   ]
)

注意,该链接是一个存档文件,也可以是此答案中所述的特定版本或存储库的分支。另外,请参见使用其他存储库主机的答案。

就我所知,更新依赖关系的最简单方法是pip install -I .从目录中安装软件包时使用。

As I just had to do the same thing, I found another way to do this as pip‘s --process-dependency-links are scheduled to be removed in pip 19.0 according to this comment.

pip 18.1 includes the following feature

Allow PEP 508 URL requirements to be used as dependencies.

From the description of PEP 508, the syntax for such URL dependencies looks like:

A minimal URL based lookup:

pip @ https://github.com/pypa/pip/archive/1.3.1.zip#sha1=da9234ee9982d4bbb3c72346a6de940a148ea686

So in your setup.py it would look like

setup(
   ...
   install_requires = [
   ...
   'python-gearman @ https://github.com/mtai/python-gearman/archive/master.zip'
   ...
   ]
)

Notice, the link is an archive file and could also be a specific release or branch of a repository as described in this answer. Also, see that answer for working with other repository hosts.

To the best of my knowledge, the easiest way to update the dependency is by using pip install -I . when installing your package from its directory.


回答 3

Vanilla setuptools不支持直接从git存储库下载,但是您可以使用该页面上的“ 下载源”链接之一,例如:

easy_install http://github.com/mtai/python-gearman/tarball/master

Vanilla setuptools does not support downloading directly from a git repository but you can use one of the Download Source links from that page, like:

easy_install http://github.com/mtai/python-gearman/tarball/master

setuptools与distutils:为什么distutils仍然是一回事?

问题:setuptools与distutils:为什么distutils仍然是一回事?

Python就可以用于包装和说明项目工具混淆历史:这些包括distutils标准库中,distributedistutils2,和setuptools(也许更多)。看来,distributedistutils2投票赞成的停产setuptools,这让两个相互竞争的标准。

据我所知,setuptools提供了更多的选择(例如,声明依赖项,测试等)distutils,但是它没有包含在Python标准库中(还可以吗?)。

现在推荐《Python打包用户指南》 [ 1 ]:

使用setuptools定义的项目和创建源代码分发。

并说明:

尽管您可以将pure distutils用于许多项目,但它不支持定义对其他项目的依赖关系,并且缺少一些便捷实用程序来自动正确填充所提供的包元数据setuptools。在标准库之外,setuptools还为不同版本的Python提供了更一致的功能集,并且(与不同distutilssetuptools将进行更新,以在所有受支持的版本上生成即将到来的“ Metadata 2.0”标准格式。

即使对于确实选择使用的项目distutils,当pip直接从源代码(而不是从预构建的wheel文件安装)安装此类项目时,它实际上也会使用setuptools来构建您的项目。

但是,查看各种项目的setup.py文件可以发现,这似乎并不是一个实际的标准。许多软件包仍在使用,distutils而那些支持的软件包则setuptools经常setuptoolsdistutils回退导入结合使用:

try:
    from setuptools import setup
except ImportError:
    from distutils.core import setup

然后尝试找到一种写可以由setuptools和安装的安装程序的方法distutils。由于distutils不支持设置功能中的依赖关系,因此通常包括各种容易出错的依赖关系检查方式。

人们为什么还要付出额外的努力来支持distutilssetuptools标准库中没有的事实是唯一的原因吗?distutils编写仅支持setup.py文件的优点和缺点是什么setuptools

Python has a confusing history of tools that can be used to package and describe projects: these include distutils in the Standard Library, distribute, distutils2, and setuptools (and maybe more). It appears that distribute and distutils2 were discontinued in favor of setuptools, which leaves two competing standards.

To my understanding setuptools offers far more options (e.g. declaring dependencies, tests, etc.) than distutils, however it is not included in the Python standard library (yet?).

The Python Packaging User Guide[1] recommends now:

Use setuptools to define projects and create Source Distributions.

And explains:

Although you can use pure distutils for many projects, it does not support defining dependencies on other projects and is missing several convenience utilities for automatically populating package metadata correctly that are provided by setuptools. Being outside the standard library, setuptools also offers a more consistent feature set across different versions of Python, and (unlike distutils), setuptools will be updated to produce the upcoming “Metadata 2.0” standard formats on all supported versions.

Even for projects that do choose to use distutils, when pip installs such projects directly from source (rather than installing from a prebuilt wheel file), it will actually build your project using setuptools instead.

However, looking into various project’s setup.py files reveals that this does not seem to be an actual standard. Many packages still use distutils and those that support setuptools often mix setuptools with distutils e.g. by doing a fallback import:

try:
    from setuptools import setup
except ImportError:
    from distutils.core import setup

Followed by an attempt to find a way to write a setup that can be installed by both setuptools and distutils. This often includes various ways of error-prone dependency checking, since distutils does not support dependencies in the setup function.

Why are people still making the extra effort to support distutils – is the fact that setuptools is not in the standard library the only reason? What are the advantages of distutils and are there any drawbacks of writing setup.py files that only support setuptools.


回答 0

看看这个SO问题。它很好地解释了所有打包方法,并可能在某种程度上帮助您回答问题:distribute,distutils,setuptools和distutils2之间的区别?

Distutils仍然是使用Python打包的标准工具。它包含在标准库中(Python 2和Python 3.0至3.3)。它对简单的Python发行版很有用,但缺少功能。它介绍了可以在setup.py脚本中导入的distutils Python软件包。

Setuptools是为克服Distutils的限制而开发的,未包含在标准库中。它引入了一个名为easy_install的命令行实用程序。它还介绍了可以在setup.py脚本中导入的setuptools Python软件包,以及可以在您的代码中导入以查找随发行版一起安装的数据文件的pkg_resources Python软件包。它的陷阱之一是它对distutils Python软件包进行了Monkey修补。它应该与pip配合良好。最新版本于2013年7月发布。

因此,正如您所见,setuptools应该比distutils更受青睐,而且我知道您的问题是从哪里来的,但是我看不到distutils会很快失去支持,因为简单地说,它在许多情况下都与一些流行的旧版程序一起使用。 。正如您可能知道的那样,在旧版程序中更改此类操作可能会很痛苦,并且会遇到很多问题,例如不兼容,这将导致开发人员不得不重写源代码。因此,distutils是标准python库的一部分,而setuptools不是。因此,如果您现在正在创建python程序,请使用setuptools,但是请记住,如果没有distutils,setuptools将永远不存在。

Have a look at this SO question. It explains all the packaging methods very well, and might help answer your question to some extent: Differences between distribute, distutils, setuptools and distutils2?

Distutils is still the standard tool for packaging in Python. It is included in the standard library (Python 2 and Python 3.0 to 3.3). It is useful for simple Python distributions, but lacks features. It introduces the distutils Python package that can be imported in your setup.py script.

Setuptools was developed to overcome Distutils’ limitations, and is not included in the standard library. It introduced a command-line utility called easy_install. It also introduced the setuptools Python package that can be imported in your setup.py script, and the pkg_resources Python package that can be imported in your code to locate data files installed with a distribution. One of its gotchas is that it monkey-patches the distutils Python package. It should work well with pip. The latest version was released in July 2013.

So, as you can see setuptools should be preferred to distutils, and I see where your question comes from, however I don’t see distutils losing support anytime soon, as, simply put, it is used in many cases with some popular legacy programs. And as you probably know changing these sorts of things in legacy programs can be quite a pain and come with quite a few problems, for example incompatibilities, which would then lead to the developer having to rewrite the source code. So there is that, and also the fact that distutils is a part of the standard python library whereas setuptools is not. So, if you are creating a python program, in this day and age, use setuptools, however keep in mind that without distutils, setuptools would have never existed.


回答 1

事实是setuptools不在标准库中的唯一原因

那是原因之一。以下直接来自NumPysetup.py

if len(sys.argv) >= 2 and ('--help' in sys.argv[1:] or
        sys.argv[1] in ('--help-commands', 'egg_info', '--version',
                        'clean')):
    # Use setuptools for these commands (they don't work well or at all
    # with distutils).  For normal builds use distutils.
    try:
        from setuptools import setup
    except ImportError:
        from distutils.core import setup

因此,NumPy setuptools希望找到它。但是后来SciPy才这样做,直到在某些情况下被修补为更喜欢它为止distutils。引用提交日志:

Setuptools sets mode +x on the test scripts, so that Nose refuses to run
them. Better not do that.

当然,一个合并之间setuptoolsdistribute应该解决这一切在适当的时间,但许多软件包仍然需要支持的Python 2.6的安装。

is the fact that setuptools is not in the standard library the only reason

That’s one reason. The following is straight from the NumPy setup.py:

if len(sys.argv) >= 2 and ('--help' in sys.argv[1:] or
        sys.argv[1] in ('--help-commands', 'egg_info', '--version',
                        'clean')):
    # Use setuptools for these commands (they don't work well or at all
    # with distutils).  For normal builds use distutils.
    try:
        from setuptools import setup
    except ImportError:
        from distutils.core import setup

So NumPy prefers setuptools if it can find it. But then SciPy used to do this, until it was patched to prefer distutils in some situations. Citing the commit log:

Setuptools sets mode +x on the test scripts, so that Nose refuses to run
them. Better not do that.

Of course, a merger between setuptools and distribute should resolve all this in due time, but many packages still need to support Python 2.6 installations.


回答 2

尽管setuptools无疑是更好的工具集,但仍有许多原因值得我们讨论和使用distutils。

首先,distutils随处可见。如果您希望构建一个与他人共享的模块,并且没有任何复杂的要求,那么可以保证它可以在您的工作机器上使用。如果您必须支持旧版本的python,或者发现自己在陌生的环境中工作,那么这尤其重要。

其次,setuptools提供了对distutils的增强。因此,它是根据distutils工具集建模的,并从那里开始获取其所有结构。setuptools的文档假定读者熟悉distutils,并且仅记录其如何增强基本工具集。您可以想到distutils定义了方言,而setuptools增强了该方言。

我对新项目的个人方法是从我将要使用distutils的假设开始的。只有随着项目的增长需要设置工具的功能,我才进行升级。setuptools是distutils的替代品,它是对setup.py的单行更改。

There are several reasons we still talk about and use distutils, even though setuptools is without a doubt the better tool set.

Firstly, distutils is available everywhere. If you are looking to build a module for sharing with others, and don’t have any complicated requirements, it is guaranteed to be available on your work machine. This is particularly important if you have to support older versions of python, or if you find yourself working in an unfamiliar environment.

Secondly, setuptools provides enhancements to distutils. It is therefore modeled after the distutils tool set and takes all of it’s structure from there. The documentation for setuptools assumes the reader is familiar with distutils and only documents how it enhances the base tool set. You can think of it that distutils defines the dialect and setuptools enhances that dialect.

My personal approach for new projects is start with the assumption I’m going to use distutils. Only as the project grows to require a feature of setuptools do I make the upgrade. The setuptools is a drop-in-replacement for distutils, it’s a one-line change to my setup.py.


回答 3

基本上,这是由于职责划分。

setuptools不是Python标准库的一部分,因为它是由第三方而不是Python核心团队维护的。除其他外,这意味着:

  • 它不包含在核心测试套件中,也不依赖于核心功能
  • 本身并没有为附加模块设置核心标准(它们的位置,导入方式,C扩展的二进制接口等)。
  • 它独立于Python版本进行更新和发布

有效地,核心团队缩小了distutils的范围,自己保留了“核心标准”和“最小必要编译”部分,同时将超出此范围的所有东西(扩展的编译器/软件包格式/任何支持)留给了第三方。为了向后兼容,以前覆盖那些“扩展部分”的代码已过时

来自分发Python模块-Python 2.7.12文档

尽管distutils逐步淘汰了直接使用,但它仍为当前的包装和分发基础架构奠定了基础,它不仅仍然是标准库的一部分,而且其名称还可以通过其他方式(例如邮件列表的名称)保留下来。用于协调Python打包标准的开发)。

对于其他操作系统软件包同样可能提供setuptoolspip单独-由于上述原因

  • 并且因为在系统上已经存在另一个软件包管理器时,它们不是必需的-甚至对可维护性有害。

Basically, it’s due to the division of responsibilities.

setuptools is not a part of Python standard library because it’s maintained by a 3rd party rather than Python core team. Which means, among other things:

  • it isn’t covered by the core test suite and isn’t relied upon by core functionality
  • it doesn’t itself set core standards for add-on modules (their location, means of import, C extensions’ binary interface etc.).
  • it’s updated and released independently from Python releases

Effectively, the core team has narrowed down the scope of distutils, reserving the “core standards” and “minimal necessary compilation” parts for themselves while leaving all the stuff beyond that (extended compiler/package format/whatever support) to 3rd parties. The code that was previously covering those “extended parts” was left stale for backwards compatibility.

From Distributing Python Modules — Python 2.7.12 documentation:

While direct use of distutils is being phased out, it still laid the foundation for the current packaging and distribution infrastructure, and it not only remains part of the standard library, but its name lives on in other ways (such as the name of the mailing list used to coordinate Python packaging standards development).

Packages for other OSes are likewise likely to provide setuptools and pip separately – for the aforementioned reasons

  • and because they aren’t necessary – or are even detrimental for maintainability – when there’s already another package manager on the system.

为什么我不能在python中创建轮子?

问题:为什么我不能在python中创建轮子?

这是我正在运行的命令:

$ python setup.py bdist_wheel
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
   or: setup.py --help [cmd1 cmd2 ...]
   or: setup.py --help-commands
   or: setup.py cmd --help

error: invalid command 'bdist_wheel'

$ pip --version
pip 1.5.6 from /usr/local/lib/python3.4/site-packages (python 3.4)

$ python -c "import setuptools; print(setuptools.__version__)"
2.1

$ python --version
Python 3.4.1

$ which python
/usr/local/bin/python

此外,我正在使用自制的python运行Mac

这是我的setup.py脚本:https : //gist.github.com/cloudformdesign/4791c46fe7cd52eb61cd

我快要疯了-我不知道为什么这不起作用。

Here are the commands I am running:

$ python setup.py bdist_wheel
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
   or: setup.py --help [cmd1 cmd2 ...]
   or: setup.py --help-commands
   or: setup.py cmd --help

error: invalid command 'bdist_wheel'

$ pip --version
pip 1.5.6 from /usr/local/lib/python3.4/site-packages (python 3.4)

$ python -c "import setuptools; print(setuptools.__version__)"
2.1

$ python --version
Python 3.4.1

$ which python
/usr/local/bin/python

Also, I am running a mac with homebrewed python

Here is my setup.py script: https://gist.github.com/cloudformdesign/4791c46fe7cd52eb61cd

I’m going absolutely crazy — I can’t figure out why this wouldn’t be working.


回答 0

首先安装wheel软件包

pip install wheel

文档并不太清楚,但是“ wheel项目为setuptools提供了bdist_wheel命令”实际上是指“ wheel package …”

Install the wheel package first:

pip install wheel

The documentation isn’t overly clear on this, but “the wheel project provides a bdist_wheel command for setuptools” actually means “the wheel package…”.


回答 1

我也遇到了错误信息 invalid command 'bdist_wheel'

原来包setup.py使用的是distutils而不是setuptools。如下进行更改可以使我制造轮子。

#from distutils.core import setup
from setuptools import setup

I also ran into the error message invalid command 'bdist_wheel'

It turns out the package setup.py used distutils rather than setuptools. Changing it as follows enabled me to build the wheel.

#from distutils.core import setup
from setuptools import setup

回答 2

也更新您的setuptools。

pip install setuptools --upgrade

如果仍然失败,则可以尝试使用其他--force标志。

Update your setuptools, too.

pip install setuptools --upgrade

If that fails too, you could try with additional --force flag.


回答 3

在以前无法正常工作之后,我也突然遇到了这种情况,这是因为我在virtualenv中,wheel没有安装在virtualenv中。

I also ran into this all of a sudden, after it had previously worked, and it was because I was inside a virtualenv, and wheel wasn’t installed in the virtualenv.


回答 4

更新您的pip第一个:

pip install --upgrade pip

对于Python 3:

pip3 install --upgrade pip

Update your pip first:

pip install --upgrade pip

for Python 3:

pip3 install --upgrade pip

回答 5

也可能是您只有python3系统。因此,您已经通过pip3 install安装了必要的软件包,例如pip3 install wheel

您需要专门使用python3构建自己的东西。

python3 setup.py sdist
python3 setup.py bdist_wheel

干杯。

It could also be that you have a python3 system only. You therefore have installed the necessary packages via pip3 install , like pip3 install wheel.

You’ll need to build your stuff using python3 specifically.

python3 setup.py sdist
python3 setup.py bdist_wheel

Cheers.


回答 6

提出另一个答案:尝试检查您的PYTHONPATH

首先,尝试重新安装wheel

pip install wheel

这应该告诉您车轮安装在哪里,例如:

Requirement already satisfied: wheel in /usr/local/lib/python3.5/dist-packages

然后将wheel的位置添加到您的PYTHONPATH

export PYTHONPATH=$PYTHONPATH:/usr/local/lib/python3.5/dist-packages/wheel

现在,构建轮子应该可以正常工作。

python setup.py bdist_wheel

Throwing in another answer: Try checking your PYTHONPATH.

First, try to install wheel again:

pip install wheel

This should tell you where wheel is installed, eg:

Requirement already satisfied: wheel in /usr/local/lib/python3.5/dist-packages

Then add the location of wheel to your PYTHONPATH:

export PYTHONPATH=$PYTHONPATH:/usr/local/lib/python3.5/dist-packages/wheel

Now building a wheel should work fine.

python setup.py bdist_wheel

回答 7

我尝试了这里所说的一切,没有任何运气,但找到了解决方法。运行此命令后(失败):bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg

转到工具制作的临时目录(在最后一个命令的输出中给出),然后执行python setup.py bdist_wheel。该.whl文件位于dist文件夹中。

I tried everything said here without any luck, but found a workaround. After running this command (and failing) : bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg

Go to the temporary directory the tool made (given in the output of the last command), then execute python setup.py bdist_wheel. The .whl file is in the dist folder.


解释Python入口点?

问题:解释Python入口点?

我已经阅读了Pylons和Peak页面中有关蛋入口的文档,但我仍然不太了解。有人可以向我解释吗?

I’ve read the documentation on egg entry points in Pylons and on the Peak pages, and I still don’t really understand. Could someone explain them to me?


回答 0

“入口点”通常是Python包的开发人员或用户可能希望使用的函数(或其他可调用的类似函数的对象),尽管也可以提供非调用对象作为入口点(正确地在评论中指出!)。

最受欢迎的入口点是console_scripts入口点,它指向您想作为命令行工具提供给安装软件包的任何人的功能。这进入您的setup.py中,如下所示:

entry_points={
    'console_scripts': [
        'cursive = cursive.tools.cmd:cursive_command',
    ],
},

我有一个刚刚部署的名为“ cursive.tools”的软件包,我希望它提供一个可以使某些人可以从命令行运行的“ cursive”命令,例如:

$ cursive --help
usage: cursive ...

完成此操作的方法是定义一个函数,例如,cursive / tools / cmd.py中的“ cursive_command”函数如下所示:

def cursive_command():
    args = sys.argv[1:]
    if len(args) < 1:
        print "usage: ..."

等等 它应该假定已从命令行调用了它,分析了用户提供的参数,然后…好,执行该命令旨在执行的所有操作。

安装docutils软件包是一个很好的切入点使用示例:它将安装诸如将六个Python文档转换成其他格式的有用命令之类的东西。

An “entry point” is typically a function (or other callable function-like object) that a developer or user of your Python package might want to use, though a non-callable object can be supplied as an entry point as well (as correctly pointed out in the comments!).

The most popular kind of entry point is the console_scripts entry point, which points to a function that you want made available as a command-line tool to whoever installs your package. This goes into your setup.py like:

entry_points={
    'console_scripts': [
        'cursive = cursive.tools.cmd:cursive_command',
    ],
},

I have a package I’ve just deployed called “cursive.tools”, and I wanted it to make available a “cursive” command that someone could run from the command line, like:

$ cursive --help
usage: cursive ...

The way to do this is define a function, like maybe a “cursive_command” function in cursive/tools/cmd.py that looks like:

def cursive_command():
    args = sys.argv[1:]
    if len(args) < 1:
        print "usage: ..."

and so forth; it should assume that it’s been called from the command line, parse the arguments that the user has provided, and … well, do whatever the command is designed to do.

Install the docutils package for a great example of entry-point use: it will install something like a half-dozen useful commands for converting Python documentation to other formats.


回答 1

EntryPoints提供持久的,基于文件系统的对象名称注册和基于名称的直接对象导入机制(由setuptools实现)包实现)。

它们将Python对象的名称与自由格式的标识符相关联。因此,使用相同的Python安装并知道标识符的任何其他代码都可以访问具有关联名称的对象,无论该对象在何处定义。的相关联的名称可以是现有一个Python模块中的任何名称 ; 例如类,函数或变量的名称。入口点机制并不关心名称所指的是什么,只要它是可导入的即可。

例如,让我们使用一个函数(的名称)和一个假想的python模块,其全限定名称为“ myns.mypkg.mymodule”:

def the_function():
   "function whose name is 'the_function', in 'mymodule' module"
   print "hello from the_function"

入口点通过setup.py中的入口点声明进行注册。要在名为“ my_ep_func”的入口下注册the_function:

    entry_points = {
        'my_ep_group_id': [
            'my_ep_func = myns.mypkg.mymodule:the_function'
        ]
    },

如示例所示,入口点被分组。有相应的API可以查找属于一个组的所有入口点(下面的示例)。

安装软件包后(即运行“ python setup.py install”),setuptools将解析以上声明。然后将解析的信息写入特殊文件中。之后,可以使用pkg_resources APIsetuptools的一部分)来查找入口点并访问具有关联名称的对象:

import pkg_resources

named_objects = {}
for ep in pkg_resources.iter_entry_points(group='my_ep_group_id'):
   named_objects.update({ep.name: ep.load()})

在这里,setuptools读取写在特殊文件中的入口点信息。在调用pkg_resources.load()时,它找到了入口点,导入了模块(myns.mypkg.mymodule),并检索了其中定义的the_function。

假设没有相同组ID的其他入口点注册,那么调用the_function将很简单:

>>> named_objects['my_ep_func']()
hello from the_function

因此,虽然起初可能有点难以理解,但是入口点机制实际上非常易于使用。它为可插拔Python软件开发提供了有用的工具。

EntryPoints provide a persistent, filesystem-based object name registration and name-based direct object import mechanism (implemented by the setuptools package).

They associate names of Python objects with free-form identifiers. So any other code using the same Python installation and knowing the identifier can access an object with the associated name, no matter where the object is defined. The associated names can be any names existing in a Python module; for example name of a class, function or variable. The entry point mechanism does not care what the name refers to, as long as it is importable.

As an example, let’s use (the name of) a function, and an imaginary python module with a fully-qualified name ‘myns.mypkg.mymodule’:

def the_function():
   "function whose name is 'the_function', in 'mymodule' module"
   print "hello from the_function"

Entry points are registered via an entry points declaration in setup.py. To register the_function under entrypoint called ‘my_ep_func’:

    entry_points = {
        'my_ep_group_id': [
            'my_ep_func = myns.mypkg.mymodule:the_function'
        ]
    },

As the example shows, entry points are grouped; there’s corresponding API to look up all entry points belonging to a group (example below).

Upon a package installation (ie. running ‘python setup.py install’), the above declaration is parsed by setuptools. It then writes the parsed information in special file. After that, the pkg_resources API (part of setuptools) can be used to look up the entry point and access the object(s) with the associated name(s):

import pkg_resources

named_objects = {}
for ep in pkg_resources.iter_entry_points(group='my_ep_group_id'):
   named_objects.update({ep.name: ep.load()})

Here, setuptools read the entry point information that was written in special files. It found the entry point, imported the module (myns.mypkg.mymodule), and retrieved the_function defined there, upon call to pkg_resources.load().

Assuming there were no other entry point registrations for the same group id, calling the_function would then be simple:

>>> named_objects['my_ep_func']()
hello from the_function

Thus, while perhaps a bit difficult to grasp at first, the entry point mechanism is actually quite simple to use. It provides an useful tool for pluggable Python software development.


回答 2

从抽象的角度来看,入口点用于创建实现特定接口的Python可调用对象的系统范围注册表。pkg_resources中有一些API可以查看给定程序包通告哪些入口点,以及可以确定哪些程序包通告某个入口点的API。

入口点对于允许一个软件包使用另一个软件包中的插件很有用。例如,Ian Bicking的Paste项目广泛使用入口点。在这种情况下,您可以编写一个使用入口点公布其WSGI应用程序工厂的包paste.app_factory

入口点的另一个用途是枚举系统上提供某些插件功能的所有软件包。该TurboGears的 Web框架使用python.templating.engines切入点来查找已安装并可用模板库。

From abstract point of view, entry points are used to create a system-wide registry of Python callables that implement certain interfaces. There are APIs in pkg_resources to see which entry points are advertised by a given package as well as APIs to determine which packages advertise a certain entry point.

Entry points are useful for allowing one package do use plugins that are in another package. For instance, Ian Bicking’s Paste project uses entry points extensively. In this case, you can write a package that advertises its WSGI application factory using the entry point paste.app_factory.

Another use for entry points is enumerating all the packages on the system that provide some plugin functionality. The TurboGears web framework uses the python.templating.engines entry point to look up templating libraries that are installed and available.