আমি কীভাবে আমার প্যাকেজে সেটআপ.পি (সেটআপলগুলিতে) সংজ্ঞায়িত সংস্করণটি পেতে পারি?


153

আমি কীভাবে setup.pyআমার প্যাকেজ থেকে সংস্করণটি সংজ্ঞায়িত করতে পারি (এর জন্য --version, বা অন্য উদ্দেশ্যে)?


3
আমি মনে করি এটি প্রায় স্ট্যাকওভারফ্লো . com/ প্রশ্নগুলি / 458550/ … হিসাবে একই প্রশ্ন ।
জুকো

উত্তর:


246

ইতিমধ্যে ইনস্টল করা বিতরণের ইন্টারোগেট সংস্করণ স্ট্রিং

রানটাইমের সময় আপনার প্যাকেজের ভিতরে থেকে সংস্করণটি পুনরুদ্ধার করতে (আপনার প্রশ্নটি আসলে কী জিজ্ঞাসা করছে বলে মনে হচ্ছে), আপনি ব্যবহার করতে পারেন:

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

ইনস্টলের সময় ব্যবহারের জন্য স্টোর সংস্করণ স্ট্রিং

আপনি যদি অন্যভাবে 'রাউন্ড' যেতে চান (যা এখানে অন্যান্য উত্তর লেখকদের মনে হয়েছে যে আপনি জিজ্ঞাসা করেছেন বলে মনে হয়), সংস্করণটির স্ট্রিংটি একটি পৃথক ফাইলে রাখুন এবং সেই ফাইলটির বিষয়বস্তু এতে পড়ুন setup.py

আপনি আপনার প্যাকেজটিতে একটি __version__লাইন দিয়ে একটি ভার্সন.পি তৈরি করতে পারেন , তারপরে সেটআপ.পি ব্যবহার করে এটি পড়ুন execfile('mypackage/version.py'), যাতে __version__এটি সেটআপ.পি নেমস্পেসে সেট করে।

আপনি যদি খুব সহজ উপায় চান যা সমস্ত পাইথন সংস্করণ এবং এমনকী পাইথন নয় এমন ভাষাগুলির সাথে কাজ করবে যা সংস্করণ স্ট্রিংয়ের অ্যাক্সেসের প্রয়োজন হতে পারে:

উদাহরণস্বরূপ VERSION, সরল পাঠ্য ফাইলের একমাত্র বিষয়বস্তু হিসাবে সংস্করণ স্ট্রিং সংরক্ষণ করুন এবং সেই ফাইলটি পড়ুন setup.py

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

একই VERSIONফাইলটি তখন অন্য যে কোনও প্রোগ্রামে ঠিক ঠিক একই সাথে কাজ করবে, এমনকি পাইথন নয়, এবং আপনার কেবল সমস্ত প্রোগ্রামের জন্য এক জায়গায় সংস্করণ স্ট্রিং পরিবর্তন করতে হবে।

ইনস্টলের সময় রেসের শর্ত সম্পর্কে সতর্কতা

যাইহোক, এখানে অন্য উত্তরে প্রস্তাবিত হিসাবে আপনার সেটআপ.পিপি থেকে আপনার প্যাকেজটি আমদানি করবেন না: এটি আপনার পক্ষে কাজ করবে বলে মনে হবে (কারণ আপনি ইতিমধ্যে আপনার প্যাকেজের নির্ভরতা ইনস্টল করেছেন) তবে এটি আপনার প্যাকেজের নতুন ব্যবহারকারীদের উপর বিপর্যয় ডেকে আনবে py , কারণ তারা প্রথমে ম্যানুয়ালি নির্ভরতা ইনস্টল না করে আপনার প্যাকেজটি ইনস্টল করতে সক্ষম হবে না।


1
এটি কেবল এখানে সেরা উত্তর: আপনার প্যাকেজটি থেকে সংস্করণটি পেতে যদি setup.py প্রয়োজন হয় তবে এক্সিকিফিল ব্যবহার করা সর্বোত্তম উপায়।
ত্রয়োদশ

2
execfileসত্যিই ভাল কাজ করে ... কিন্তু (দুঃখিতভাবে) পাইথন 3. সাথে কাজ করে না
medmunds

9
... ঠিক আছে, পাইথন 2 এবং 3 with open('mypackage/version.py') as f: exec(f.read())এর জায়গায় ব্যবহার করুন execfile('mypackage/version.py')। (এইখান থেকে stackoverflow.com/a/437857/647002 )
medmunds

5
বিধ্বংসী কান্ড অংশটি অগত্যা সত্য নয় ... প্যাকেজটিতে আপনার init .py ফাইলের কোড আমদানি নির্ভরতা (প্রত্যক্ষ বা অপ্রত্যক্ষভাবে) থাকে তবেই তা কেবল ধ্বংস হয় ।
পাইকলার

2
এটি উল্লেখ করার মতো মনে হয় যে জাঙ্গো তাদের সেটআপ.পিতে একটি __পরম্পত__ কল করে । 'বনাম' আমদানি করে কি '__তর্কতা' এটি নিরাপদ করে? এটি তাদের কোনও সমস্যা সৃষ্টি করছে বলে মনে হয় না।
ব্যবহারকারী 1978019

33

উদাহরণ অধ্যয়ন: 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 পেতে পারে।

সমাধান: à লা setuptools

আপনি একটি সমন্বয় ব্যবহার করেন open, execএবং জন্য একটি অভি প্রদান 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'

এইভাবে, সংস্করণটি মডিউলটির সাথে প্রেরণ করা হবে এবং সেটআপ করার সময় আপনার কোনও সমস্যা নেই যা মডিউলটি হারিয়েছে যা নির্ভরশীলতা হারিয়েছে (এখনও ইনস্টল করা হয়নি) import


2
এটি সবচেয়ে যুক্তিসঙ্গত সমাধান বলে মনে হচ্ছে কারণ এটি কেবল অনুমোদিত নির্ভরতার উপর নির্ভর করে।
ডগওয়েদার

প্যাকেজ সংস্করণের ধারণা থেকে setup.py ফাইল বিভিন্ন সংজ্ঞায়িত করা হয় সংস্করণ ?
চলক

16

সেরা কৌশলটি হ'ল __version__আপনার পণ্য কোডটিতে সংজ্ঞা দেওয়া, তারপরে সেখান থেকে সেটআপ.পিতে আমদানি করুন। এটি আপনাকে আপনার চলমান মডিউলটিতে পড়তে পারে এমন মান দেয় এবং এটির সংজ্ঞা দেওয়ার জন্য কেবলমাত্র একটি জায়গা রয়েছে।

Setup.py এ থাকা মানগুলি ইনস্টল করা হয় না এবং ইনস্টলেশন পরে সেটআপ.পিপি আটকে থাকে না।

কভারেজ.পিতে আমি যা করেছি (উদাহরণস্বরূপ):

# coverage/__init__.py
__version__ = "3.2"


# setup.py
from coverage import __version__

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

আপডেট (2017): Cover.py সংস্করণটি পেতে নিজেকে আর আমদানি করে না। আপনার নিজের কোড আমদানি এটিকে আনইনস্টেবল করে তুলতে পারে, কারণ আপনার পণ্য কোডটি নির্ভরতা আমদানির চেষ্টা করবে, যা এখনও ইনস্টল করা হয়নি, কারণ সেটআপ.পিই তাদের ইনস্টল করে।


4
@ ইভান: আমি নিশ্চিত না যে আপনি "উত্স থেকে কেবল সেই মানটি টানুন" সম্পর্কে কী পাচ্ছেন। এতে থাকা ফাইলটি __version__যদি কোনওভাবে ভাঙা হয় তবে আপনার আমদানিও নষ্ট হয়ে যাবে। পাইথন জানে না যে কীভাবে আপনি চান সেই বিবৃতিগুলি কীভাবে ব্যাখ্যা করতে হয়। @ পিজেবি ঠিক বলেছেন: যদি আপনার মডিউলটিকে অন্য মডিউলগুলি আমদানি করতে হয় তবে সেগুলি এখনও ইনস্টল করা নাও হতে পারে, এবং এটি বিশৃঙ্খলাবদ্ধ হবে। এই কৌশলটি কার্যকর হয় যদি আপনি সতর্ক হন যে আমদানি অন্যান্য আমদানির দীর্ঘ শৃঙ্খলা সাবধান করে না।
নেড ব্যাচেল্ডার

1
@ নেড ব্যাচেল্ডার আমি নিশ্চিত যে আপনি উত্স ফাইলটিতে কোনও আমদানির আগে সংস্করণটি রাখেন এবং কেবল 'মডিউল আমদানি সংস্করণ থেকে' যদি উত্স ফাইলটি প্রয়োজন হয় তার চেয়ে বেশি লেক্স করে না। তা ছাড়া কে ভাঙা কোড প্রকাশ করতে চলেছে? প্যাকেজের যদি নির্ভরতা প্রয়োজন হয় তবে সেটআপলগুলি ব্যবহার করুন বা বছরের পরবর্তীতে ডিস্টুটিস 2 প্রকাশের জন্য অপেক্ষা করুন।
ইভান প্লেস

15
@ ইভান, আমি দুঃখিত, তবে আপনি আংশিক ফাইল আমদানি সম্পর্কে ভুল are দীর্ঘ মডিউলটির শেষে একটি মুদ্রণ বিবৃতি রাখার চেষ্টা করুন এবং ফাইলটিতে সংজ্ঞায়িত প্রথম ভেরিয়েবলটি আমদানি করুন। মুদ্রণ বিবৃতি কার্যকর করা হবে।
নেড ব্যাচেল্ডার

23
আমি এর জন্য পড়েছি, তবে @ পিজেবি সম্পূর্ণরূপে সঠিক: "উপায় দ্বারা, আপনার সেটআপ থেকে আপনার প্যাকেজটি আমদানি করবেন না pyপি এখানে অন্য উত্তরে প্রস্তাবিত: এটি আপনার পক্ষে কাজ করবে বলে মনে হবে (কারণ আপনি ইতিমধ্যে আপনার প্যাকেজের নির্ভরতা ইনস্টল করে রেখেছেন) ), তবে এটি আপনার প্যাকেজের নতুন ব্যবহারকারীর উপর বিপর্যয় ডেকে আনবে, কারণ তারা প্রথমে ম্যানুয়ালি নির্ভরতা ইনস্টল না করে আপনার প্যাকেজটি ইনস্টল করতে সক্ষম হবে না। " নেড, আপনি আপনার উত্তরে একটি সতর্কতা যুক্ত করতে আপত্তি করবেন?
ডাঃ জানু-ফিলিপ গেহর্কেকে

1
ভালো লেগেছে @ জানুয়ারী-PhilipGehrcke যখন আমি PyPI এই মত একটি setup.py ফাইল আপলোড এবং তারপর pip install <packagename>, আমি নিম্নলিখিত ত্রুটির পাবেন: ImportError: No module named <packagename>। দয়া করে আপনার পাঠকদের সাবধান করুন যে প্যাকেজটি ইতিমধ্যে ইনস্টল করা হয়নি এমন পরিবেশে আপনি সেটআপ.পি ফাইলগুলি চালাতে পারবেন না!
hobs

14

আপনার প্রশ্নটি কিছুটা অস্পষ্ট, তবে আমি মনে করি আপনি কী জিজ্ঞাসা করছেন এটি কীভাবে নির্দিষ্ট করা যায়।

আপনাকে এর __version__মতো সংজ্ঞা দিতে হবে:

__version__ = '1.4.4'

এবং তারপরে আপনি নিশ্চিত করতে পারেন যে সেটআপ.পি আপনার সুনির্দিষ্ট সংস্করণটি সম্পর্কে জানেন:

% ./setup.py --version
1.4.4

9

আমি এই উত্তরগুলির সাথে খুশি ছিলাম না ... সেটআপটুলগুলি প্রয়োজন ছিল না, বা কোনও একক ভেরিয়েবলের জন্য একটি সম্পূর্ণ পৃথক মডিউল তৈরি করতে চাইনি, তাই আমি এগুলি নিয়ে এসেছি।

আপনি যখন নিশ্চিত হন যে মূল মডিউলটি পিপ 8 স্টাইলে রয়েছে এবং সেভাবেই থাকবে:

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

সেটআপ.পি এটি কিছুটা কুৎসিত হলে কোনও সমস্যা নয় a


আপডেট: যথেষ্ট মজার আমি সাম্প্রতিক বছরগুলিতে এ থেকে সরে এসেছি এবং প্যাকেজের নামে পৃথক একটি ফাইল ব্যবহার শুরু করেছি meta.py। আমি সেখানে প্রচুর মেটা ডেটা রেখেছি যাতে আমি প্রায়শই পরিবর্তন করতে চাই। সুতরাং, কেবল একটি মানের জন্য নয়।


+1 টি। এটি তুলনামূলকভাবে সহজ, সংস্করণ নম্বরটি এক জায়গায় রাখে, এটি ধারণের জন্য পৃথক ফাইলের প্রয়োজন হয় না এবং সেটআপ.পিতে পাইথন মডিউলটির আমদানি নির্ভরতা চাপায় না। এমনকি আমি কোনও প্রোগ্রামে কনটেক্সট ম্যানেজারকে সেটআপ.পি হিসাবে স্বল্পকালীন হিসাবে বিরক্ত করব না।
әɹsәɹoɈ

রেগেক্স ব্যবহার করার চেয়ে অনেক ভাল, ধন্যবাদ। উত্স থেকে স্বয়ংক্রিয়ভাবে পূরণ করতে আপনি ast.get_docstring()কিছু .split('\n')[0].strip()ইত্যাদি ব্যবহার করতে পারেন description। সিঙ্কে রাখার জন্য আর একটি কম জিনিস
হারিয়েছে

4

আপনার উত্স ট্রিতে একটি ফাইল তৈরি করুন, যেমন আপনার বাসেডির / আপনার প্যাকেজ / _version.py এ। এই ফাইলটিতে কোডের একক লাইন থাকতে দেওয়া যাক:

__version__ = "1.1.0-r4704"

তারপরে আপনার সেটআপ.পি-তে, সেই ফাইলটি খুলুন এবং এর মতো সংস্করণ নম্বরটি পার্স করুন:

verstr = "অজানা"
চেষ্টা করে দেখুন:
    verstrline = উন্মুক্ত ('yourpackage / _version.py', "আরটি")। পড়ুন ()
পরিবেশের ত্রুটি বাদে:
    পাস # ঠিক আছে, কোনও সংস্করণ ফাইল নেই।
অন্য:
    VSRE = r "^ __ সংস্করণ__ = ['\"] ([[' \ "] *) ['\"] "
    mo = re.search (VSRE, verstrline, re.M)
    যদি মো:
        verstr = mo.group (1)
    অন্য:
        রানটাইমআরারের উত্থাপন ("আপনার প্যাকেজ / _version.py সংস্করণ খুঁজে পেতে অক্ষম")

অবশেষে, yourbasedir/yourpackage/__init__.pyআমদানিতে এই জাতীয় রূপান্তর:

__ রূপান্তর__ = "অজানা"
চেষ্টা করে দেখুন:
    _আপনার আমদানি __ রূপান্তর__ থেকে
আমদানি তদন্ত ব্যতীত:
    # আমরা এমন একটি গাছে দৌড়াচ্ছি যার _version.py নেই, তাই আমাদের সংস্করণটি কী তা আমরা জানি না।
    পাস

কোডগুলি উদাহরণ করে যা এটি করে "পিউটিল" প্যাকেজ যা আমি রক্ষণ করি। (পাইপিআই বা গুগল অনুসন্ধান দেখুন - স্ট্যাকওভারফ্লো আমাকে এই উত্তরে একটি হাইপারলিঙ্ক অন্তর্ভুক্ত করা থেকে বঞ্চিত করছে))

@ পিজেবি ঠিক বলেছেন যে আপনার নিজের প্যাকেজটির নিজস্ব সেটআপ.পিপি থেকে আমদানি করা উচিত নয়। এটি কাজ করবে যখন আপনি এটি পরীক্ষা করার সময় একটি নতুন পাইথন ইন্টারপ্রেটার তৈরি করে এবং প্রথমে সেটআপ.পি চালিয়ে python setup.pyযাবেন : তবে এমন কিছু ক্ষেত্রে রয়েছে যখন এটি কাজ করবে না। এটি কারণ import youpackage"আপনার প্যাকেজ" নামের একটি ডিরেক্টরিটির জন্য বর্তমান চলমান ডিরেক্টরিটি পড়ার অর্থ নয়, এর অর্থ একটি বর্তমান "আপনার প্যাকেজ" এর sys.modulesজন্য বর্তমানটি অনুসন্ধান করা এবং তারপরে এটি না থাকলে বিভিন্ন জিনিস করা। সুতরাং এটি যখন আপনি করেন তখন সর্বদা কাজ করে python setup.pyকারণ আপনার একটি তাজা, খালি রয়েছে sys.modules, তবে এটি সাধারণভাবে কাজ করে না।

উদাহরণস্বরূপ, পাইপেক্স যদি কোনও অ্যাপ্লিকেশন প্যাকেজিংয়ের প্রক্রিয়ার অংশ হিসাবে আপনার সেটআপ.পি চালিত করে তবে কী হবে? আমি এরকম একটি মামলা দেখেছি যেখানে পাই 2 এক্সই কোনও প্যাকেজে ভুল সংস্করণ নম্বর রাখবে কারণ প্যাকেজটি তার সংস্করণ নম্বর পেয়েছিলimport myownthingএটির সেটআপ.পিতে, তবে পাইপেক্স এক্স রান চলাকালীন সেই প্যাকেজের একটি আলাদা সংস্করণ আগে আমদানি করা হয়েছিল। তেমনি, যদি সেটআপটুলগুলি, ইজি_ইনস্টল, বিতরণ, বা ডিস্টিল 2 আপনার নিজের উপর নির্ভর করে অন্য প্যাকেজ ইনস্টল করার প্রক্রিয়ার অংশ হিসাবে আপনার প্যাকেজটি তৈরি করার চেষ্টা করছে? তারপরে আপনার প্যাকেজটি সেটআপ.পি মূল্যায়ন করার সময় আমদানিযোগ্য কিনা, বা ইতিমধ্যে আপনার প্যাকেজটির কোনও সংস্করণ রয়েছে যা এই পাইথন ইন্টারপ্রেটারের জীবনকালে আমদানি করা হয়েছে, বা আপনার প্যাকেজটি আমদানি করার জন্য প্রথমে অন্য প্যাকেজগুলি ইনস্টল করার প্রয়োজন আছে কিনা? , বা পার্শ্ব-প্রতিক্রিয়া রয়েছে, ফলাফল পরিবর্তন করতে পারে। পাইথন প্যাকেজগুলি পুনরায় ব্যবহারের চেষ্টা করার সাথে আমার বেশ কয়েকটি লড়াই হয়েছিল যার ফলে পাইপেক্স এবং সেটআপটুলের মতো সরঞ্জামগুলির জন্য সমস্যা দেখা দিয়েছে কারণ তাদের সেটআপ.পি প্যাকেজটি এর সংস্করণ নম্বরটি আবিষ্কার করার জন্য নিজেই আমদানি করে।

যাইহোক, এই কৌশলটি আপনার জন্য স্বয়ংক্রিয়ভাবে yourpackage/_version.pyফাইলটি তৈরি করার সরঞ্জামগুলির সাথে দুর্দান্ত খেলছে , উদাহরণস্বরূপ আপনার পুনর্বিবেচনা নিয়ন্ত্রণ ইতিহাস পড়ে এবং সংশোধন নিয়ন্ত্রণের ইতিহাসের সাম্প্রতিক ট্যাগের ভিত্তিতে একটি সংস্করণ নম্বর লিখে। এখানে এমন একটি সরঞ্জাম রয়েছে যা এটি ডার্কগুলির জন্য করে: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst এবং এখানে একটি কোড স্নিপেট রয়েছে যা গিটের জন্য একই কাজ করে: http: // github .com / ওয়ার্নার / পাইথন-ECDSA / ফোঁটা / 0ed702a9d4057ecf33eea969b8cf280eaccd89a1 / setup.py # L34


3

এটি নিয়মিত এক্সপ্রেশন ব্যবহার করে এবং মেটাডেটা ক্ষেত্রগুলির উপর নির্ভর করে এর মতো ফর্ম্যাট রাখার জন্য এটিও কাজ করা উচিত:

__fieldname__ = 'value'

আপনার সেটআপ.পাইয়ের শুরুতে নিম্নলিখিতটি ব্যবহার করুন:

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

আমি উপরে যেমন বলেছি, কেবল স্ট্রিমস্প্লিট ব্যবহার করুন :)
আরাউজো

ওহ godশ্বর না আপনি পাইথনে পাইথন পার্স করছেন? খুব কমপক্ষে, শিশু, eval () ব্যবহার করুন।
অস্পষ্ট

3
দরিদ্র পরামর্শ ত্রুটিপূর্ণ, eval এত সহজ যখন এড়ানো উচিত।
গ্রিঙ্গো সুভে

3

এর মতো কাঠামো সহ:

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

যেখানে version.py রয়েছে:

__version__ = 'version_string'

আপনি সেটআপ.ইপিতে এটি করতে পারেন :

import sys

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

from version import __version__

এটি আপনার মাইমোডুল / __ init__.py এ আপনার যা কিছু নির্ভরতা রয়েছে তাতে কোনও সমস্যা হবে না


2

কোনও ফাইল আমদানি এড়াতে (এবং এর ফলে কোডটি কার্যকর করা) কেউ পার্স করতে পারে এবং 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মডিউল ফেরতের শীর্ষ স্তরে নিয়োগ স্ট্রিং মান। ডান দিকটি হয় স্ট্রিং বা টিপল হতে পারে।


3
এটি চতুর এবং জটিল দেখায় :) কেবলমাত্র একটি অ্যাসাইনমেন্ট যুক্ত একটি _version.py ফাইল থাকা ওপেন-রিড-স্ট্রিংস্প্লিট দিয়ে পার্স করা সহজ হবে।
এরিক আরাউজো

এই পোস্ট করার জন্য ধন্যবাদ। আমি মনে করি এই পরিস্থিতির পক্ষে এটি খুব জটিল, তবে কীভাবে কেউ সমস্যাটির কাছে যেতে পারে তা বোঝার জন্য খুব সহায়ক। আমি পছন্দ করি যে এটি সংস্করণ নম্বরটি এক জায়গায় রাখে, এটি ধারণ করতে আলাদা ফাইলের প্রয়োজন হয় না এবং সেটআপ স্ক্রিপ্টে পাইথন মডিউলটির আমদানি নির্ভরতা চাপায় না।
әɹsәɹoɈ

2

বিড়ালের চামড়ার হাজারো উপায় রয়েছে - এটি আমার:

# 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

@ 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

2

এখন এটি মোটামুটি এবং কিছু পরিমার্জনের প্রয়োজন (পিকেজি_সোর্সায় যে কোনও অনাবৃত সদস্যের কল থাকতে পারে যা আমি মিস করেছি) তবে আমি কেবল দেখতে পাচ্ছি না কেন এটি কাজ করে না, বা কেন কেউ আজও এটি প্রস্তাব করেনি (গুগলিং চারপাশে রয়েছে) এটি চালু করা হয়নি) ... নোট করুন যে এটি পাইথন ২.x এবং এর জন্য প্রয়োজন pkg_res્રોস (দীর্ঘশ্বাস):

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

আমরা আমাদের প্যাকেজ সম্পর্কে মেটা তথ্য লাগাতে চেয়েছিল pypackageryমধ্যে __init__.py, কিন্তু না যেহেতু এটি তৃতীয় পক্ষের নির্ভরতা রয়েছে যেমন পি জে Eby ইতিমধ্যে (তার উত্তর এবং race অবস্থা সংক্রান্ত সতর্কবার্তা দেখুন) নির্দিষ্ট পারে না।

আমরা এটি একটি পৃথক মডিউল তৈরি করে সমাধান করেছি 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_modules। অন্যথায়, আপনি ইনস্টলেশনটি এটিকে আমদানি করতে পারবেন না কারণ প্যাকেজযুক্ত বিতরণটির অভাব হবে would


1

সহজ এবং সোজা, 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()

পাইথন সঙ্গে এই পরিক্ষিত 2.7, 3.3, 3.4, 3.5, 3.6এবং 3.7লিনাক্স, উইন্ডোজ এবং ম্যাক OS এ। আমি আমার প্যাকেজটিতে ব্যবহার করেছি যা এই সমস্ত প্ল্যাটফর্মের জন্য ইন্টিগ্রেশন এবং ইউনিট টেস্ট রয়েছে। আপনি ফলাফলগুলি এখানে .travis.ymlএবং appveyor.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পাইথন 2.7এবং উভয়ই ইউনিকোড ত্রুটিগুলি পরিচালনা করতে মডিউলটি ব্যবহার করতে পারেন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 ) )

আপনি যদি পাইথন সি এক্সটেনশনগুলি ব্যবহার করে সি / সি ++ তে পাইথন মডিউলটি 100% লিখছেন তবে আপনি একই জিনিসটি করতে পারেন তবে পাইথনের পরিবর্তে সি / সি ++ ব্যবহার করছেন।

এই ক্ষেত্রে, নিম্নলিখিত তৈরি করুন 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. পাইথন ওপেন ফাইল ত্রুটি
  2. সি এপিআই থেকে পাইথন মডিউলে গ্লোবাল সংজ্ঞা দিন
  3. কীভাবে সেটআপলগুলি / বিতরণ সহ প্যাকেজ ডেটা অন্তর্ভুক্ত করবেন?
  4. https://github.com/lark-parser/lark/blob/master/setup.py#L4
  5. একই নামে সেটআপ টলস প্যাকেজ এবং এক্সট_মডিউলগুলি কীভাবে ব্যবহার করবেন?
  6. প্যাকেজ ডেটার অংশ হিসাবে ডিস্ট ইউজ (সেটআপ.পি) ব্যবহার করে উপ-ডিরেক্টরিগুলি অন্তর্ভুক্ত করা সম্ভব?

1

ইনডেক্স প্যাকেজগুলির জন্য সার্ভারে প্যাকেজ এবং ফাইল নামকরণের কনভেনশন স্থাপন করুন:

পিপ গতিশীল সংস্করণ রূপান্তর জন্য উদাহরণ:

  • win:

    • test_pkg-1.0.0-cp36-cp36m-win_amd64.whl
    • test_pkg-1.0.0-py3.6, জয়-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(
    version=_get_version(),
    name=NAME,
    description=DESCRIPTION,
    long_description=LONG_DESCRIPTION,
    classifiers=CLASSIFIERS,

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

)

0

আমি নীচের মতো পরিবেশের পরিবর্তনশীল ব্যবহার করছি

সংস্করণ = 0.0.0 অজগর সেটআপ.পি এসডিস্ট বিডিস্ট_হিল

সেটআপ.পিতে


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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.