Làm cách nào tôi có thể nhận phiên bản được xác định trong setup.py (setuptools) trong gói của mình?


Câu trả lời:


246

Hỏi phiên bản chuỗi phân phối đã được cài đặt

Để truy xuất phiên bản từ bên trong gói của bạn khi chạy (câu hỏi của bạn thực sự đang hỏi), bạn có thể sử dụng:

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

Lưu trữ chuỗi phiên bản để sử dụng trong khi cài đặt

Nếu bạn muốn đi theo hướng khác (dường như là những gì các tác giả câu trả lời khác ở đây dường như nghĩ rằng bạn đang hỏi), hãy đặt chuỗi phiên bản vào một tệp riêng và đọc nội dung của tệp đó setup.py.

Bạn có thể tạo một phiên bản gói trong gói của mình bằng một __version__dòng, sau đó đọc nó từ setup.py bằng cách sử dụng execfile('mypackage/version.py')nó để đặt __version__trong không gian tên setup.py.

Nếu bạn muốn một cách đơn giản hơn nhiều sẽ hoạt động với tất cả các phiên bản Python và ngay cả các ngôn ngữ không phải Python có thể cần truy cập vào chuỗi phiên bản:

Lưu trữ chuỗi phiên bản dưới dạng nội dung duy nhất của tệp văn bản thuần, có tên ví dụ VERSIONvà đọc tệp đó trong thời gian setup.py.

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

Sau đó, cùng một VERSIONtệp sẽ hoạt động chính xác trong bất kỳ chương trình nào khác, ngay cả những người không phải là Python và bạn chỉ cần thay đổi chuỗi phiên bản ở một nơi cho tất cả các chương trình.

Cảnh báo về tình trạng cuộc đua trong khi cài đặt

Nhân tiện, KHÔNG nhập gói của bạn từ setup.py như được đề xuất trong câu trả lời khác ở đây: có vẻ như nó sẽ hiệu quả với bạn (vì bạn đã cài đặt các phụ thuộc của gói), nhưng nó sẽ phá hỏng người dùng mới của gói của bạn , vì họ sẽ không thể cài đặt gói của bạn mà không cài đặt thủ công các phụ thuộc trước.


1
Đây đơn giản là câu trả lời tốt nhất ở đây: cách tốt nhất là sử dụng execfile nếu bạn cần setup.py để lấy phiên bản từ gói của bạn.
thứ

2
execfilehoạt động rất tốt ... nhưng (đáng buồn thay) không hoạt động với Python 3.
medmunds

9
... OK, sử dụng Python 2 3 with open('mypackage/version.py') as f: exec(f.read())thay cho execfile('mypackage/version.py'). (Từ stackoverflow.com/a/437857/647002 )
medmunds

5
Phần wreak havoc không nhất thiết đúng ... nó sẽ chỉ phá hỏng nếu tệp init .py của bạn trong gói có phụ thuộc nhập mã (trực tiếp hoặc gián tiếp).
Pykler

2
Có vẻ như đáng nói rằng django thực hiện một cuộc gọi __import__ trong setup.py của họ . Liệu '__import__' so với 'nhập' có làm cho điều này an toàn không? Nó dường như không gây ra cho họ bất kỳ vấn đề.
dùng1978019

33

nghiên cứu ví dụ: mymodule

Hãy tưởng tượng cấu hình này:

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

Sau đó tưởng tượng một số kịch bản thông thường nơi bạn có phụ thuộc và setup.pytrông giống như:

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

Và một ví dụ __init__.py:

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

Và ví dụ myclasses.py:

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

vấn đề # 1: nhập mymoduletrong khi thiết lập

Nếu bạn setup.pynhập mymodulesau đó trong quá trình thiết lập, rất có thể bạn sẽ nhận được ImportError. Đây là một lỗi rất phổ biến khi gói của bạn có phụ thuộc. Nếu gói của bạn không có các phụ thuộc khác ngoài nội dung, bạn có thể an toàn; tuy nhiên đây không phải là một thực hành tốt. Lý do cho điều đó là nó không phải là bằng chứng trong tương lai; nói ngày mai mã của bạn cần tiêu thụ một số phụ thuộc khác.

vấn đề # 2: của tôi __version__đâu?

Nếu bạn hardcode __version__trong setup.pysau đó nó có thể không phù hợp với phiên bản mà bạn sẽ xuất xưởng vào mô-đun của bạn. Để thống nhất, bạn sẽ đặt nó ở một nơi và đọc nó từ cùng một nơi khi bạn cần. Sử dụng importbạn có thể nhận được vấn đề # 1.

giải pháp: à la setuptools

Bạn sẽ sử dụng một sự kết hợp của open, execvà cung cấp một dict cho execđến các biến add:

# 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__'],
    ...)

Và trong mymodule/version.pyphiên bản phơi bày:

__version__ = 'some.semantic.version'

Bằng cách này, phiên bản được cung cấp cùng với mô-đun và bạn không gặp sự cố trong quá trình thiết lập khi cố gắng nhập mô-đun thiếu phụ thuộc (chưa được cài đặt).


2
Đây có vẻ là giải pháp hợp lý nhất vì nó chỉ dựa vào các phụ thuộc được phép.
Dogweather 7/2/2015

Là khái niệm về phiên bản gói được định nghĩa trong tệp setup.py khác với phiên bản ?
biến

16

Kỹ thuật tốt nhất là xác định __version__mã sản phẩm của bạn, sau đó nhập mã vào setup.py từ đó. Điều này cung cấp cho bạn một giá trị bạn có thể đọc trong mô-đun đang chạy của mình và chỉ có một nơi để xác định nó.

Các giá trị trong setup.py không được cài đặt và setup.py không dính xung quanh sau khi cài đặt.

Những gì tôi đã làm (ví dụ) trong vùng phủ sóng:

# coverage/__init__.py
__version__ = "3.2"


# setup.py
from coverage import __version__

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

CẬP NHẬT (2017): cover.txt không còn nhập chính nó để có phiên bản. Nhập mã của riêng bạn có thể làm cho nó có thể gỡ cài đặt được, bởi vì mã sản phẩm của bạn sẽ cố gắng nhập các phụ thuộc chưa được cài đặt, bởi vì setup.py là thứ cài đặt chúng.


4
@Evan: Tôi không chắc chắn những gì bạn đang nhận được về "chỉ lấy giá trị đó từ nguồn". Nếu tệp có __version__trong đó bị hỏng theo một cách nào đó, thì quá trình nhập của bạn cũng sẽ bị hỏng. Python không biết làm thế nào để chỉ diễn giải các câu bạn muốn. @pjither là đúng: nếu mô-đun của bạn cần nhập các mô-đun khác, chúng có thể chưa được cài đặt và nó sẽ hỗn loạn. Kỹ thuật này hoạt động nếu bạn cẩn thận rằng việc nhập không tạo ra một chuỗi dài các lần nhập khác.
Ned Batchelder

1
@Ned Batchelder Tôi khá chắc chắn rằng, nếu bạn đặt phiên bản trước bất kỳ lần nhập nào trong tệp nguồn và chỉ đến 'từ phiên bản nhập mô-đun', nó sẽ không lex thêm bất kỳ tệp nguồn nào cần thiết. Bên cạnh đó, ai sẽ phát hành mã bị hỏng? Nếu gói cần phụ thuộc, hãy sử dụng setuptools hoặc đợi phát hành distutils2 sau này trong năm.
Evan Plaice

15
@Evan, tôi xin lỗi, nhưng bạn đã sai khi nhập một phần tệp. Hãy thử đặt một câu lệnh in ở cuối mô-đun dài và nhập biến đầu tiên được xác định trong tệp. Câu lệnh in sẽ thực thi.
Ned Batchelder

23
Tôi đã yêu, nhưng @pjither hoàn toàn đúng: "Nhân tiện, KHÔNG nhập gói của bạn từ setup.py như được đề xuất trong câu trả lời khác ở đây: có vẻ như nó sẽ hoạt động cho bạn (vì bạn đã cài đặt các phụ thuộc của gói ), nhưng nó sẽ tàn phá những người dùng mới của gói của bạn, vì họ sẽ không thể cài đặt gói của bạn mà không cài đặt thủ công các phụ thuộc trước. " Ned, bạn có phiền khi thêm một cảnh báo vào câu trả lời của bạn không?
Tiến sĩ Jan-Philip Gehrcke

1
Giống như @ Jan-PhilipGehrcke khi tôi tải lên tệp setup.py như thế này lên PyPI và sau đó pip install <packagename>, tôi gặp lỗi sau : ImportError: No module named <packagename>. Vui lòng CẢNH BÁO cho độc giả của bạn rằng bạn không thể chạy các tệp setup.py như thế này trên các môi trường nơi gói chưa được cài đặt!
hobs

14

Câu hỏi của bạn hơi mơ hồ, nhưng tôi nghĩ những gì bạn đang hỏi là làm thế nào để xác định nó.

Bạn cần xác định __version__như vậy:

__version__ = '1.4.4'

Và sau đó, bạn có thể xác nhận rằng setup.py biết về phiên bản bạn vừa chỉ định:

% ./setup.py --version
1.4.4

9

Tôi không hài lòng với những câu trả lời này ... không muốn yêu cầu setuptools, cũng không tạo một mô-đun riêng cho một biến duy nhất, vì vậy tôi đã đưa ra những điều này.

Vì khi bạn chắc chắn mô-đun chính theo kiểu pep8 và sẽ giữ nguyên như vậy:

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

Nếu bạn muốn cẩn thận hơn và sử dụng trình phân tích cú pháp thực sự:

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 là một phần của mô-đun bỏ đi nên không thành vấn đề nếu nó hơi xấu.


Cập nhật: đủ buồn cười Tôi đã chuyển đi từ này trong những năm gần đây và bắt đầu sử dụng một tệp riêng trong gói được gọi meta.py. Tôi đặt rất nhiều dữ liệu meta vào đó mà tôi có thể muốn thay đổi thường xuyên. Vì vậy, không chỉ cho một giá trị.


+1. Nó tương đối đơn giản, giữ số phiên bản ở một nơi, không yêu cầu một tệp riêng để chứa nó và không áp đặt các phụ thuộc nhập khẩu của mô-đun python vào setup.py. Tôi thậm chí sẽ không bận tâm với trình quản lý bối cảnh trong một chương trình có thời gian ngắn như setup.py.
ʇsәɹoɈ

Nhiều đẹp hơn so với sử dụng regex, cảm ơn. Bạn cũng có thể sử dụng ast.get_docstring()với một số .split('\n')[0].strip()vv để tự động điền descriptiontừ nguồn. Một điều ít để giữ đồng bộ
mất

4

Tạo một tệp trong cây nguồn của bạn, ví dụ như trong yourbasingir / yourpackage / _version.py. Để tập tin đó chỉ chứa một dòng mã, như thế này:

__version__ = "1.1.0-r4704"

Sau đó, trong setup.py của bạn, mở tệp đó và phân tích số phiên bản như thế này:

verstr = "không xác định"
thử:
    verstrline = open ('yourpackage / _version.py', "rt"). read ()
ngoại trừ môi trườngError:
    vượt qua # Được rồi, không có tệp phiên bản.
khác:
    VSRE = r "^ __ phiên bản__ = ['\"] ([^' \ "] *) ['\"] "
    mo = re.search (VSRE, verstrline, re.M)
    nếu mo:
        verstr = mo.group (1)
    khác:
        nâng cao RuntimeError ("không thể tìm thấy phiên bản trong gói của bạn / _version.py")

Cuối cùng, trong quá trình yourbasedir/yourpackage/__init__.pynhập _version như thế này:

__version__ = "không xác định"
thử:
    từ nhập _version __version__
ngoại trừ ImportError:
    # Chúng tôi đang chạy trong một cái cây không có _version.py, vì vậy chúng tôi không biết phiên bản của chúng tôi là gì.
    vượt qua

Một ví dụ về mã thực hiện điều này là gói "pyutil" mà tôi duy trì. (Xem PyPI hoặc tìm kiếm google - stackoverflow không cho phép tôi bao gồm một siêu liên kết đến nó trong câu trả lời này.)

@pjither đúng là bạn không nên nhập gói của mình từ setup.py của chính nó. Điều đó sẽ hoạt động khi bạn kiểm tra nó bằng cách tạo một trình thông dịch Python mới và thực thi setup.py trong điều đầu tiên: python setup.pynhưng có những trường hợp khi nó không hoạt động. Đó là bởi vì import youpackagekhông có nghĩa là đọc thư mục làm việc hiện tại cho một thư mục có tên là "yourpackage", nó có nghĩa là tìm trong hiện tại sys.modulesmột khóa "yourpackage" và sau đó làm nhiều việc khác nếu nó không có ở đó. Vì vậy, nó luôn hoạt động khi bạn làm python setup.pybởi vì bạn có một sản phẩm mới, trống rỗng sys.modules, nhưng điều này không hoạt động nói chung.

Ví dụ: điều gì sẽ xảy ra nếu py2exe đang thực thi setup.py của bạn như là một phần của quy trình đóng gói ứng dụng? Tôi đã thấy một trường hợp như thế này trong đó py2exe sẽ đặt sai số phiên bản trên một gói vì gói đó lấy số phiên bản từimport myownthingtrong setup.py, nhưng một phiên bản khác của gói đó trước đây đã được nhập trong quá trình chạy py2exe. Tương tự, điều gì sẽ xảy ra nếu setuptools, easy_install, phân phối hoặc distutils2 đang cố gắng xây dựng gói của bạn như là một phần của quá trình cài đặt một gói khác phụ thuộc vào bạn? Sau đó, liệu gói của bạn có thể được nhập vào thời điểm mà cài đặt của nó được đánh giá hay không, hoặc đã có phiên bản của gói đã được nhập trong vòng đời của trình thông dịch Python này hay chưa, hoặc việc nhập gói của bạn yêu cầu các gói khác phải được cài đặt trước hoặc có tác dụng phụ, có thể thay đổi kết quả. Tôi đã gặp nhiều khó khăn khi cố gắng sử dụng lại các gói Python gây ra sự cố cho các công cụ như py2exe và setuptools vì setup.py của họ nhập chính gói đó để tìm số phiên bản của nó.

Nhân tiện, kỹ thuật này chơi độc đáo với các công cụ để tự động tạo yourpackage/_version.pytệp cho bạn, ví dụ bằng cách đọc lịch sử kiểm soát sửa đổi của bạn và viết ra một số phiên bản dựa trên thẻ gần đây nhất trong lịch sử kiểm soát sửa đổi. Đây là một công cụ thực hiện điều đó cho các darcs: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst và đây là một đoạn mã thực hiện điều tương tự cho git: http: // github .com / warner / python-ecdsa / blob / 0ed702a9d4057ecf33eea969b8cf280eaccd89a1 / setup.py # L34


3

Điều này cũng sẽ hoạt động, sử dụng các biểu thức thông thường và tùy thuộc vào các trường siêu dữ liệu để có định dạng như thế này:

__fieldname__ = 'value'

Sử dụng như sau khi bắt đầu thiết lập của bạn:

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

Sau đó, bạn có thể sử dụng siêu dữ liệu trong tập lệnh của mình như thế này:

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

Như tôi đã nói ở trên, chỉ cần sử dụng str.split :)
Éric Araujo

Trời ơi không. Bạn đang phân tích cú pháp Python trong Python? Ít nhất, sử dụng eval (), con.
Slacy

3
Lời khuyên tồi tệ, eval nên tránh khi quá dễ dàng.
Gringo Suave

3

Với cấu trúc như thế này:

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

nơi phiên bản có chứa:

__version__ = 'version_string'

Bạn có thể làm điều này trong setup.py :

import sys

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

from version import __version__

Điều này sẽ không gây ra bất kỳ vấn đề nào với bất kỳ sự phụ thuộc nào bạn có trong mô hình của bạn / __ init__.py


2

Để tránh nhập tệp (và do đó thực thi mã của nó), người ta có thể phân tích cú pháp và khôi phục versionthuộc tính từ cây cú pháp:

# 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

Mã này cố gắng tìm __version__hoặc VERSIONgán ở mức cao nhất của trả về mô-đun là giá trị chuỗi. Phía bên phải có thể là một chuỗi hoặc một tuple.


3
Điều đó có vẻ thông minh và phức tạp :) Sẽ đơn giản hơn khi chỉ có một tệp _version.py chứa một nhiệm vụ và phân tích cú pháp đó với open-read-str.split.
Éric Araujo

Cảm ơn đã đăng bài này. Tôi nghĩ nó quá phức tạp cho tình huống này, nhưng rất hữu ích để hiểu cách người ta có thể tiếp cận vấn đề. Tôi thích rằng nó giữ số phiên bản ở một nơi, không yêu cầu một tệp riêng để chứa nó và không áp đặt các phụ thuộc nhập khẩu của mô-đun python vào tập lệnh thiết lập.
ʇsәɹoɈ

2

Có một ngàn cách để lột da một con mèo - đây là của tôi:

# 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.")

2

Dọn dẹp https://stackoverflow.com/a/12413800 từ @ 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

2

Bây giờ điều này là thô thiển và cần một số tinh chỉnh (thậm chí có thể có một cuộc gọi thành viên chưa được phát hiện trong pkg_resource mà tôi đã bỏ lỡ), nhưng tôi đơn giản không hiểu tại sao điều này không hoạt động, và tại sao không ai đề xuất nó cho đến nay (Googling xung quanh có không bật cái này lên) ... lưu ý rằng đây là Python 2.x và sẽ yêu cầu pkg_resource (thở dài):

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

2

Chúng tôi muốn đưa thông tin meta về gói của chúng tôi pypackageryvào __init__.py, nhưng không thể vì nó có sự phụ thuộc của bên thứ ba như PJ Eby đã chỉ ra (xem câu trả lời của anh ấy và cảnh báo về tình trạng cuộc đua).

Chúng tôi đã giải quyết nó bằng cách tạo một mô-đun riêng chỉpypackagery_meta.py chứa thông tin meta:

"""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'

sau đó nhập thông tin meta vào packagery/__init__.py:

# ...

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

# ...

và cuối cùng đã sử dụng nó trong 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'],
 )

Bạn phải bao gồm pypackagery_metatrong gói của bạn với py_modulesđối số thiết lập. Mặt khác, bạn không thể nhập nó khi cài đặt vì bản phân phối đóng gói sẽ thiếu nó.


1

Đơn giản và dễ dàng, tạo một tệp được gọi source/package_name/version.pyvới nội dung sau:

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

Sau đó, trên tệp của bạn source/package_name/__init__.py, bạn nhập phiên bản cho người khác sử dụng:

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

Bây giờ, bạn có thể đặt cái này lên 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()

Thử nghiệm điều này với Python 2.7, 3.3, 3.4, 3.5, 3.63.7trên Linux, Windows và Mac OS. Tôi đã sử dụng trên gói của mình có Kiểm tra tích hợp và đơn vị cho tất cả các nền tảng luận văn. Bạn có thể xem kết quả từ .travis.ymlappveyor.ymltại đây:

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

Một phiên bản thay thế đang sử dụng trình quản lý bối cảnh:

#!/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 ) )

Bạn cũng có thể sử dụng codecsmô-đun để xử lý các lỗi unicode cả trên 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 ) )

Nếu bạn đang viết mô-đun Python 100% bằng C / C ++ bằng phần mở rộng Python C, bạn có thể làm điều tương tự, nhưng sử dụng C / C ++ thay vì Python.

Trong trường hợp này, tạo như sau 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'],
            )
        ],
    )

Mà đọc phiên bản từ tệp version.h:

const char* __version__ = "1.0.12";

Nhưng, đừng quên tạo tệp MANIFEST.inđể bao gồm version.htệp:

include README.md
include LICENSE.txt

recursive-include source *.h

Và nó được tích hợp vào ứng dụng chính với:

#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__ ) );

    ...
}

Người giới thiệu:

  1. lỗi python mở tập tin
  2. Xác định toàn cầu trong mô-đun Python từ API C
  3. Làm cách nào để bao gồm dữ liệu gói với setuptools / phân phối?
  4. https://github.com/lark-parser/lark/blob/master/setup.py#L4
  5. Làm cách nào để sử dụng gói setuptools và ext_modules có cùng tên?
  6. Có thể bao gồm các thư mục con bằng cách sử dụng dist utils (setup.py) như một phần của dữ liệu gói không?

1

triển khai gói đến máy chủ và quy ước đặt tên tệp cho các gói chỉ mục:

ví dụ cho chuyển đổi phiên bản động của pip:

  • thắng lợi:

    • 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

Tìm thiết lập mẫu gọi các phiên bản pip động khớp từ git commit

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

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

)

0

Tôi đang sử dụng một biến môi trường như dưới đây

VERSION = 0.0.0 python setup.py sdist bdist_wheel

Trong setup.py


import os

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

Để kiểm tra tính nhất quán với phiên bản đóng gói, tôi đang sử dụng tập lệnh bên dưới.

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.