57

假设您有一个名为的项目proj,并且在该项目中您具有以下结构:

proj/
  dists/
  doc/
  src/
    __init__.py
    xyz.py
    abc.py
  test/
  setup.py

如您所见,项目的所有内容都在src子文件夹中。如何从src文件夹中制作 distutils 分发包?

按照教程,我的天真想法是这样写setup.py

#omitting basics
setup(
   name='proj',
   packages=['src'],
   package_dir={'proj':'src'}
)

但是在将生成的包安装到我的系统后,我仍然需要 importsrc.xyz而不是proj.xyz,这将是目标和预期结果。

4

6 回答 6

21

您可以通过将 Python 包文件放入proj/目录来修复它:

proj/
  src/
    proj/
      __init__.py
      xyz.py
      abc.py
  setup.py

并更改setup.py为:

# ...
setup(
   name='proj',
   packages=['proj'],
   package_dir={'':'src'}
)

distutils 不需要它,但其他工具可能期望__init__.py文件的父目录名称与 Python 包名称相同,即proj在这种情况下。

于 2013-01-20T02:37:52.020 回答
18

这是由于此处报告的 setuptools 中的错误:https ://github.com/pypa/setuptools/issues/250

基本上,它确实可以工作,但不能在开发模式下工作。现在你有3个解决方案:

  • src将包符号链接为proj(并在提交时忽略它),它将开箱即用但很脏
  • 从 更改srcproj
  • proj在其中创建一个子目录src并使用以下选项:
包= ['项目'],
package_dir={'proj': 'src/proj'},
于 2016-03-15T13:33:20.267 回答
3

正确的设置是:

#omitting basics
setup(
   name='proj',
   packages=['proj'],
   package_dir={'proj':'src'}
)

src 文件夹应包含__init__.py(如果文件为空,则默认导出所有内容)

在另一个项目中:requirements.txt:

../relativePathToProject或姓名package:version

于 2021-11-05T13:32:01.277 回答
3

您可以先使用find_packages查找所有包名src,然后手动将它们重命名为所需的名称。最后,使用该package_dir选项指定包约定。

import re

from setuptools import find_packages, setup

PACKAGE_NAME = 'proj'
SOURCE_DIRECTORY = 'src'
SOURCE_PACKAGE_REGEX = re.compile(rf'^{SOURCE_DIRECTORY}')

source_packages = find_packages(include=[SOURCE_DIRECTORY, f'{SOURCE_DIRECTORY}.*'])
proj_packages = [SOURCE_PACKAGE_REGEX.sub(PACKAGE_NAME, name) for name in source_packages]

setup(
    name=PACKAGE_NAME,
    packages=proj_packages,
    package_dir={PACKAGE_NAME: SOURCE_DIRECTORY},
    ...
)
于 2021-07-08T15:32:37.780 回答
1

基于Jfs 的 Answer,如果您像我一样已经建立了一个您不想/由于其他原因无法更改的目录结构,一种解决方案是临时复制所有要打包的代码,然后构建它。

这是一个带有目标的生成文件,它将 src 中的所有文件复制到一个临时位置,然后调用 setup.py 来构建包,最后自行清理。

SRC_DIR='src'
TEMP_PACKAGE_DIR='your_shiny_package'

package: prepare_package_dir build_package deploy_package remove_package_dir

# I'm using gemfury, your deployment will probably look different
deploy_package:
    twine upload --repository fury dist/* --verbose

clean_package:
    rm -r dist || echo 'dist removed'
    rm -r ${TEMP_PACKAGE_DIR}.egg-info || echo 'egg-info removed'

build_package: clean_package
    python setup.py sdist

prepare_package_dir:
    mkdir ${TEMP_PACKAGE_DIR}
    cp -R ${SRC_DIR}/* ${TEMP_PACKAGE_DIR}/
    mv ${TEMP_PACKAGE_DIR} ${SRC_DIR}/${TEMP_PACKAGE_DIR}

remove_package_dir:
    rm -rf ${SRC_DIR}/${TEMP_PACKAGE_DIR}

然后一个 setup.py 看起来有点像这样:


setup(
    name='your_shiny_package',
    version=version,
    description='some great package...',
    long_description=readme,
    url='https://whereever',
    author='you',
    packages=['src/your_shiny_package'],
    install_requires=parse_all_requirements(),
    zip_safe=False,
    include_package_data=True)

可能不是最有效的,但可以避免您永久重组整个回购。

用法:只需make package作为构建管道的一部分或手动调用。

于 2020-10-14T21:30:51.593 回答
-5

您可以在调用函数之前尝试将src文件夹添加到:PYTHONPATHsetup

import sys, os
src_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), 'src')
sys.path.append(src_path)

而且,为了安全起见,您可以更改工作目录:

os.chdir(src_path)

在那之后,它应该一切都好。

用于打包应用程序的其他一些工具从内部支持这一点。我以为是 setuptools,结果是 PyInstaller。但基本上,这就是应该做的,足以让你的包直接导入。

原来 distutils 有这个package_dir指令。那是你应该使用的,但它可能只通过将你的包添加到PYTHONPATH.

于 2013-01-20T01:52:11.850 回答