13

在创建 Python 包时,我需要在“sdist”阶段运行自己的脚本。我写了以下脚本。你知道更好的方法吗?您能否推荐一个更好的或链接到 setuptools 上的官方文档,其中已经解释了这一时刻?

import subprocess
import sys

from setuptools import setup, find_packages, os

if 'sdist' in sys.argv:
    cwd = os.getcwd()
    os.chdir('website/static/stylesheets/')
    result = subprocess.call("scss --update --compass ./*.scss", shell=True)
    if result != 0:
        exit(1)
    os.chdir(cwd)

setup(name = "site",
    author="Vladimir Ignatev",
    author_email="mail@gmail.com",
    version="0.1",
    packages=find_packages(),
    include_package_data=True,
    zip_safe=True,
)
4

2 回答 2

6

虽然这来得很晚,但这里有一个解决方案建议。

基本上,它只是通过添加自定义逻辑并将其注册到 setup 函数中来继承distutils'命令。不幸的是,这个主题的官方文档有点模糊和简洁扩展 Distutils至少提供了一个小例子。我发现阅读包中模块的代码以了解实际命令的实现方式要好得多。sdistdistutils.command

要执行任意命令,您可以使用distutils.cmd.Command::spawn执行传递的输入字符串的方法,DistutilsExecError如果命令的退出代码不为零,则引发 a:

from distutils.command.sdist import sdist as sdist_orig
from distutils.errors import DistutilsExecError

from setuptools import setup  


class sdist(sdist_orig):

    def run(self):
        try:
            self.spawn(['ls', '-l'])
        except DistutilsExecError:
            self.warn('listing directory failed')
        super().run()


setup(name='spam',
    version='0.1',
    packages=[],
    cmdclass={
        'sdist': sdist
    }
)

运行上面的设置脚本会产生:

$ python setup.py sdist
running sdist
ls -l
total 24
-rw-r--r--  1 hoefling  staff   52 23 Dez 19:06 MANIFEST
drwxr-xr-x  3 hoefling  staff   96 23 Dez 19:06 dist
-rw-r--r--  1 hoefling  staff  484 23 Dez 19:07 setup.py
running check
...
writing manifest file 'MANIFEST'
creating spam-0.1
making hard links in spam-0.1...
hard linking setup.py -> spam-0.1
Creating tar archive
removing 'spam-0.1' (and everything under it)

重用命令

这是(虽然简化了)我们在项目中使用的命令的真实示例,该命令用于 NodeJS 项目并调用yarn

import distutils
import os
import pathlib
import setuptools

_YARN_CMD_SEP = ';'

_HELP_MSG_SUBCMD = (
    'yarn subcommands to execute (separated '
    'by {})'.format(_YARN_CMD_SEP)
)

_HELP_MSG_PREFIX = (
    'path to directory containing package.json. '
    'If not set, current directory is assumed.'
)


class yarn(setuptools.Command):

    description = ('runs yarn commands. Assumes yarn is '
                   'already installed by the user.')

    user_options = [
        ('subcommands=', None, _HELP_MSG_SUBCMD),
        ('prefix=', None, _HELP_MSG_PREFIX),
    ]

    def initialize_options(self) -> None:
        self.subcommands = []
        self.prefix = None  # type: pathlib.Path

    def finalize_options(self) -> None:
        self.subcommands = [
            cmd.strip() for cmd in str(self.subcommands).split(self._YARN_CMD_SEP)
        ]
        self.prefix = pathlib.Path(self.prefix) if self.prefix else pathlib.Path()

    def run(self) -> None:
        cwd = pathlib.Path().absolute()
        os.chdir(str(self.prefix.absolute()))  # change to prefix dir
        for cmd in self.subcommands:
            self.announce('running yarn {} ...'.format(cmd), level=distutils.log.INFO)
            self.spawn(['yarn'] + cmd.split(' '))
        os.chdir(str(cwd))  # change back to our previous dir

示例用法:

$ python setup.py yarn --prefix=. --subcommands="add leftpad; remove leftpad"
running yarn
running yarn add leftpad ...
yarn add leftpad
yarn add v1.3.2
warning package.json: No license field
warning No license field
[1/4]   Resolving packages...
[2/4]   Fetching packages...
[3/4]   Linking dependencies...
[4/4]   Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
└─ leftpad@0.0.1
warning No license field
✨  Done in 0.33s.
running yarn remove leftpad ...
yarn remove leftpad
yarn remove v1.3.2
warning package.json: No license field
[1/2] Removing module leftpad...
[2/2] Regenerating lockfile and installing missing dependencies...
warning No license field
success Uninstalled packages.
✨  Done in 0.13s.

你也可以yarn在你的命令链中使用它作为所有其他命令:python setup.py yarn test sdist等等。

于 2017-12-23T20:07:43.990 回答
-7

也许试试distutils我认为它比 setuptools 更新。

于 2013-07-26T18:40:41.233 回答