zipapp --- مدیریت بایگانی‌های zip قابل‌اجرای پایتون

اضافه شده در نسخه‌ی 3.5.

کد منبع: Lib/zipapp.py


این ماژول ابزارهایی را برای مدیریت ایجاد پرونده‌های zip حاوی کد پایتون فراهم می‌کند، که می‌توانند به‌طور مستقیم توسط مفسر پایتون اجرا شوند. این ماژول هم یک رابط خط فرمان و هم یک API پایتون را فراهم می‌کند.

مثال ساده

مثال زیر نشان می‌دهد که چگونه می‌توان از رابط خط فرمان برای ایجاد یک بایگانی قابل اجرا از یک پوشه حاوی کد پایتون استفاده کرد. هنگام اجرا، بایگانی تابع main را از ماژول myapp درون بایگانی اجرا می‌کند.

$ python -m zipapp myapp -m "myapp:main"
$ python myapp.pyz
<output from myapp>

رابط خط فرمان

هنگام فراخوانی به‌عنوان یک برنامه از خط فرمان، از قالب زیر استفاده می‌شود:

$ python -m zipapp source [options]

اگر source یک پوشه باشد، این کار یک بایگانی از محتویات source ایجاد می‌کند. اگر source یک پرونده باشد، باید یک بایگانی باشد، و به بایگانی مقصد کپی می‌شود (یا محتویات خط shebang آن در صورتی که گزینه --info مشخص شده باشد نمایش داده می‌شود).

گزینه‌های زیر قابل‌درک هستند:

-o <output>, --output=<output>

خروجی را در پرونده‌ای به نام output بنویسید. اگر این گزینه مشخص نشده باشد، نام پرونده خروجی همان نام ورودی source خواهد بود و پسوند .pyz به آن افزوده می‌شود. اگر نام پرونده صریحی داده شود، همان‌طور که هست استفاده می‌شود (بنابراین در صورت نیاز، باید پسوند .pyz لحاظ شود).

اگر source یک بایگانی باشد، باید نام پرونده خروجی مشخص شود (و در این حالت، output نباید با source یکسان باشد).

-p <interpreter>, --python=<interpreter>

یک خط #! به بایگانی اضافه کنید که interpreter را به‌عنوان دستور برای اجرا مشخص می‌کند. همچنین، در POSIX، بایگانی را قابل‌اجرا کنید. پیش‌فرض این است که هیچ خط #! نوشته نشود و پرونده قابل‌اجرا نشود.

-m <mainfn>, --main=<mainfn>

یک پرونده __main__.py در بایگانی بنویسید که mainfn را اجرا می‌کند. آرگومان mainfn باید به قالب "pkg.mod:fn" باشد، که در آن "pkg.mod" یک بسته/ماژول در بایگانی است، و "fn" یک شیء فراخوانی‌پذیر در ماژول داده‌شده است. پرونده __main__.py آن شیء فراخوانی‌پذیر را اجرا خواهد کرد.

هنگام کپی کردن یک بایگانی نمی‌توان --main را مشخص کرد.

-c, --compress

فشرده‌سازی پرونده‌ها با متد deflate، که اندازه‌ی پرونده خروجی را کاهش می‌دهد. به‌طور پیش‌فرض، پرونده‌ها به‌صورت فشرده‌نشده در آرشیو ذخیره می‌شوند.

--compress هنگام کپی کردن یک بایگانی هیچ تأثیری ندارد.

اضافه شده در نسخه‌ی 3.7.

--info

مفسر تعبیه‌شده در بایگانی را برای اهداف تشخیصی نمایش می‌دهد. در این حالت، تمام گزینه‌های دیگر نادیده گرفته می‌شوند و SOURCE باید یک بایگانی باشد، نه یک پوشه.

-h, --help

یک پیام کوتاه کاربرد را چاپ می‌کند و خارج می‌شود.

API پایتون

این ماژول دو تابع سهولت‌بخش تعریف می‌کند:

zipapp.create_archive(source, target=None, interpreter=None, main=None, filter=None, compressed=False)

یک بایگانی برنامه از source ایجاد کنید. منبع می‌تواند هر یک از موارد زیر باشد:

  • نام یک پوشه، یا یک path-like object که به یک پوشه اشاره می‌کند؛ در این صورت یک آرشیو برنامه‌ی جدید از محتوای آن پوشه ایجاد خواهد شد.

  • نام یک پرونده آرشیو برنامه موجود، یا یک path-like object که به چنین پرونده‌ای اشاره دارد؛ در این صورت پرونده به مقصد کپی می‌شود (و به‌گونه‌ای تغییر داده می‌شود که مقدار داده‌شده برای آرگومان interpreter را بازتاب دهد). نام پرونده باید در صورت لزوم شامل پسوند .pyz باشد.

  • یک شیء پرونده که برای خواندن در حالت بایت باز شده است. محتوای پرونده باید یک بایگانی برنامه باشد، و فرض می‌شود شیء پرونده در ابتدای بایگانی قرار دارد.

آرگومان target تعیین می‌کند که بایگانی حاصل کجا نوشته خواهد شد:

  • اگر نام یک پرونده یا یک path-like object باشد، بایگانی در آن پرونده نوشته خواهد شد.

  • اگر یک شیء پرونده باز باشد، بایگانی در آن شیء پرونده نوشته خواهد شد؛ این شیء باید برای نوشتن در حالت بایت باز باشد.

  • اگر مقصد حذف شود (یا None باشد)، منبع باید یک پوشه باشد و مقصد پرونده‌ای با همان نام منبع خواهد بود که پسوند .pyz به آن افزوده می‌شود.

آرگومان interpreter نام مفسر پایتون را مشخص می‌کند که بایگانی با آن اجرا خواهد شد. این مقدار به‌صورت یک خط «shebang» در ابتدای بایگانی نوشته می‌شود. در POSIX، این خط توسط سیستم‌عامل تفسیر خواهد شد و در ویندوز توسط راه‌انداز پایتون مدیریت خواهد شد. حذف interpreter منجر به نوشته نشدن هیچ خط «shebang» می‌شود. اگر مفسری مشخص شده باشد و مقصد یک نام پرونده باشد، بیت اجرایی (executable bit) پرونده مقصد تنظیم خواهد شد.

آرگومان main نام یک شیء فراخوانی‌پذیر را مشخص می‌کند که به‌عنوان برنامه اصلی بایگانی استفاده خواهد شد. این آرگومان فقط زمانی می‌تواند مشخص شود که منبع یک پوشه باشد و منبع از قبل حاوی پرونده __main__.py نباشد. آرگومان main باید به‌شکل "pkg.module:callable" باشد و بایگانی با ایمپورت کردن "pkg.module" و اجرای شیء فراخوانی‌پذیر داده‌شده بدون آرگومان اجرا خواهد شد. اگر منبع یک پوشه باشد و حاوی پرونده __main__.py نباشد، مشخص نکردن main خطا است، زیرا در غیر این صورت بایگانی حاصل قابل اجرا نخواهد بود.

آرگومان اختیاری filter یک تابع کال‌بک را مشخص می‌کند که یک شیء Path به آن داده می‌شود؛ این شیء مسیر پرونده در حال اضافه شدن (نسبت به پوشه منبع) را نشان می‌دهد. این تابع باید در صورتی که پرونده قرار است اضافه شود، True را برگرداند.

آرگومان اختیاری compressed مشخص می‌کند که پرونده‌ها فشرده شوند یا خیر. اگر روی True تنظیم شود، پرونده‌های درون بایگانی با روش deflate فشرده می‌شوند؛ در غیر این صورت، پرونده‌ها به‌صورت غیرفشرده ذخیره می‌شوند. این آرگومان هنگام کپی کردن یک بایگانی موجود تأثیری ندارد.

اگر برای source یا target یک شیء پرونده مشخص‌شده باشد، مسئولیت بستن آن پس از فراخوانی create_archive با فراخواننده است.

هنگام کپی کردن یک بایگانی موجود، اشیای پرونده ارائه‌شده فقط به متدهای read و readline یا write نیاز دارند. هنگام ایجاد یک بایگانی از یک پوشه، اگر مقصد یک شیء پرونده باشد، به کلاس zipfile.ZipFile داده می‌شود و باید متدهای مورد نیاز آن کلاس را فراهم کند.

تغییر یافته در نسخه‌ی 3.7: پارامترهای filter و compressed افزوده شدند.

zipapp.get_interpreter(archive)

مفسر مشخص‌شده در خط #! در ابتدای آرشیو را برمی‌گرداند. اگر خط #! وجود نداشته باشد، None را برمی‌گرداند. آرگومان archive می‌تواند یک نام پرونده یا یک شیء شبه‌پرونده باشد که برای خواندن در حالت بایت‌ها باز شده است. فرض می‌شود در ابتدای آرشیو قرار دارد.

مثال‌ها

یک پوشه را در قالب یک بایگانی بسته‌بندی کنید و آن را اجرا کنید.

$ python -m zipapp myapp
$ python myapp.pyz
<output from myapp>

همین کار را می‌توان با استفاده از تابع create_archive() انجام داد:

>>> import zipapp
>>> zipapp.create_archive('myapp', 'myapp.pyz')

برای قابل‌اجرا کردن مستقیم برنامه روی POSIX، یک مفسر برای استفاده مشخص کنید.

$ python -m zipapp myapp -p "/usr/bin/env python"
$ ./myapp.pyz
<output from myapp>

برای جایگزینی خط shebang در یک بایگانی موجود، یک بایگانی اصلاح‌شده را با استفاده از تابع create_archive() ایجاد کنید:

>>> import zipapp
>>> zipapp.create_archive('old_archive.pyz', 'new_archive.pyz', '/usr/bin/python3')

برای به‌روزرسانی پرونده به‌صورت درجا، جایگزینی را در حافظه با استفاده از یک شیء BytesIO انجام دهید و سپس منبع را بازنویسی کنید. توجه داشته باشید که هنگام بازنویسی یک پرونده به‌صورت درجا، این خطر وجود دارد که یک خطا باعث از دست رفتن پرونده اصلی شود. این کد در برابر چنین خطاهایی محافظت نمی‌کند، اما کد تولیدی باید این کار را انجام دهد. همچنین، این روش تنها در صورتی کار می‌کند که آرشیو در حافظه جا شود:

>>> import zipapp
>>> import io
>>> temp = io.BytesIO()
>>> zipapp.create_archive('myapp.pyz', temp, '/usr/bin/python2')
>>> with open('myapp.pyz', 'wb') as f:
>>>     f.write(temp.getvalue())

تعیین مفسر

توجه داشته باشید که اگر مفسری را مشخص کنید و سپس آرشیو برنامه‌ی خود را توزیع کنید، باید اطمینان حاصل کنید که مفسر استفاده‌شده قابل حمل است. راه‌انداز پایتون برای ویندوز از رایج‌ترین شکل‌های خط #! POSIX پشتیبانی می‌کند، اما مسائل دیگری نیز وجود دارند که باید آن‌ها را در نظر بگیرید:

  • اگر از "/usr/bin/env python" (یا سایر شکل‌های دستور "python"، مانند "/usr/bin/python") استفاده می‌کنید، باید در نظر داشته باشید که کاربران شما ممکن است پایتون 2 یا پایتون 3 را به‌عنوان پیش‌فرض خود داشته باشند و کد خود را به‌گونه‌ای بنویسید که تحت هر دو نسخه کار کند.

  • اگر از یک نسخه مشخص استفاده کنید، برای مثال "/usr/bin/env python3"، برنامه شما برای کاربرانی که آن نسخه را ندارند کار نخواهد کرد. (اگر کد خود را با Python 2 سازگار نکرده‌اید، ممکن است همین چیزی باشد که می‌خواهید).

  • راهی برای بیان "python X.Y or later" وجود ندارد، بنابراین در استفاده از یک نسخه‌ی دقیق مانند "/usr/bin/env python3.4" احتیاط کنید، زیرا برای مثال برای کاربران Python 3.5 ناچار خواهید بود خط shebang خود را تغییر دهید.

معمولاً باید از "/usr/bin/env python2" یا "/usr/bin/env python3" استفاده کنید، بسته به اینکه کد شما برای پایتون 2 نوشته شده باشد یا 3.

ایجاد برنامه‌های مستقل با zipapp

با استفاده از ماژول zipapp، می‌توان برنامه‌های پایتون خودکفا ایجاد کرد که قابل توزیع بین کاربران نهایی هستند؛ این کاربران تنها نیاز دارند نسخه مناسبی از پایتون روی سیستم خود نصب باشد. کلید این کار قرار دادن همه وابستگی‌های برنامه به همراه کد برنامه در بایگانی است.

مراحل ساخت یک بایگانی مستقل به شرح زیر است:

  1. برنامه خود را مانند حالت عادی در یک پوشه ایجاد کنید، به‌گونه‌ای که یک پوشه myapp داشته باشید که شامل یک پرونده __main__.py و هر کد پشتیبان برنامه باشد.

  2. تمام وابستگی‌های برنامه شما را با استفاده از pip در پوشه myapp نصب کنید:

    $ python -m pip install -r requirements.txt --target myapp
    

    (این فرض بر این است که نیازمندی‌های پروژه شما در یک پرونده requirements.txt قرار دارد؛ در غیر این صورت، می‌توانید صرفاً وابستگی‌ها را به‌صورت دستی در خط فرمان pip فهرست کنید).

  3. بسته‌بندی برنامه با استفاده از:

    $ python -m zipapp -p "interpreter" myapp
    

این کار یک پرونده اجرایی مستقل تولید می‌کند که می‌توان آن را روی هر ماشینی که مفسر مناسب در دسترس باشد اجرا کرد. برای جزئیات، تعیین مفسر را ببینید. می‌توان آن را به‌صورت یک پرونده واحد به کاربران تحویل داد.

در یونیکس، پرونده myapp.pyz به‌همین شکل قابل اجرا است. اگر نام فرمان «ساده» را ترجیح می‌دهید، می‌توانید نام پرونده را تغییر دهید تا پسوند .pyz حذف شود. در ویندوز، پرونده myapp.pyz[w] به این دلیل قابل اجرا است که مفسر پایتون هنگام نصب، پسوندهای پرونده .pyz و .pyzw را ثبت می‌کند.

نکات مهم

اگر برنامه شما به بسته‌ای وابسته است که شامل یک افزونه C است، آن بسته نمی‌تواند از یک پرونده zip اجرا شود (این یک محدودیت سیستم‌عامل است، زیرا کد اجرایی باید در سامانه فایل‌بندی موجود باشد تا بارگذار سیستم‌عامل بتواند آن را بارگذاری کند). در این صورت، می‌توانید آن وابستگی را از پرونده zip خارج کنید و یا از کاربران خود بخواهید آن را نصب کرده باشند، یا آن را همراه پرونده zip خود توزیع کنید و کدی به __main__.py خود اضافه کنید تا پوشه‌ی حاوی ماژول از حالت فشرده خارج‌شده را در sys.path قرار دهد. در این صورت، باید اطمینان حاصل کنید که دودویی‌های مناسب (binaries) برای معماری(های) هدف خود را توزیع می‌کنید (و در صورت لزوم، نسخه صحیح را برای افزودن به sys.path در زمان ران‌تایم، بر اساس ماشین کاربر انتخاب کنید).

قالب بایگانی برنامه‌ی Zip پایتون

پایتون از نسخه 2.6 قادر به اجرای پرونده‌های zip حاوی یک پرونده __main__.py بوده است. برای اجرا شدن توسط پایتون، یک بایگانی برنامه صرفاً باید یک پرونده zip استاندارد حاوی یک پرونده __main__.py باشد که به‌عنوان نقطه ورود برنامه اجرا خواهد شد. همان‌طور که برای هر اسکریپت پایتون معمول است، والد اسکریپت (در این مورد پرونده zip) در sys.path قرار می‌گیرد و بنابراین می‌توان ماژول‌های بیشتری را از پرونده zip ایمپورت کرد.

قالب پرونده zip اجازه می‌دهد داده‌های دلخواه به ابتدای یک پرونده zip اضافه شوند. قالب برنامه zip از این قابلیت برای افزودن یک خط «shebang» استاندارد POSIX به ابتدای پرونده استفاده می‌کند (#!/path/to/interpreter).

بنابراین، قالب برنامه‌ی zip پایتون به‌طور رسمی عبارت است از:

  1. یک خط shebang اختیاری، شامل نویسه‌های b'#!' است که پس از آن‌ها نام مفسر و سپس یک نویسه‌ی خط جدید (b'\n') می‌آید. نام مفسر می‌تواند هر چیزی باشد که برای پردازش «shebang» سیستم‌عامل یا راه‌انداز پایتون در ویندوز قابل‌قبول باشد. نام مفسر باید در ویندوز با UTF-8 و در POSIX با sys.getfilesystemencoding() کدگذاری شود.

  2. داده‌های استاندارد zipfile، همان‌طور که توسط ماژول zipfile تولید می‌شوند. محتوای zipfile باید شامل پرونده‌ای به نام __main__.py باشد (که باید در «ریشه» zipfile باشد - یعنی نمی‌تواند در یک زیرپوشه باشد). داده‌های zipfile می‌توانند فشرده یا غیرفشرده باشند.

اگر یک آرشیو برنامه دارای خط shebang باشد، ممکن است بیت اجرایی آن در سیستم‌های POSIX تنظیم شده باشد تا بتوان آن را مستقیماً اجرا کرد.

هیچ الزامی وجود ندارد که ابزارهای این ماژول برای ایجاد بایگانی‌های برنامه استفاده شوند - این ماژول برای سهولت است، اما بایگانی‌های برنامه در قالب بالا که به هر وسیله‌ای ایجاد شده باشند، برای پایتون قابل‌قبول هستند.