بسته‌بندی Namespace در پایتون

در مقاله بسته‌بندی و انتشار کد در مورد نحوه انتشار بسته‌ها و کتابخانه‌های پایتونی در PyPi به شیوه بسته‌بندی معمولی توضیحاتی داده شد. (در صورتیکه با این مفهوم آشنا نیستید مقاله زیر را مطالعه کنید). در این مقاله بصورت جزیی‌تر در مورد نحوه بسته‌بندی درصورتیکه بخواهیم چندین پکیج را با هم ارائه دهیم صحبت خواهیم کرد.

Namespace Package

پایتون از نسخه 3.3 نوع خاص دیگری از بسته‌بندی به نام بسته‌بندی فضای نامی (Namespace Package) معرفی کرده است که به شما این امکان می‌دهد دو بسته با بیشتر را تحت یک بسته انتشار دهید.

برای نمونه اگر ساختار پکیج زیر را داشته باشید:

mynamespace/
	__init__.py
	
	subpkg_a/
		__init__.py
    	module.py
    	
	subpkg_b/
		__init__.py
    	module.py
    	
 setup.py

و از این پکیج در کدهای خود بصورت زیر استفاده می کنید:

from mynamespace import subpkg_a
from mynamespace import subpkg_b

بسته‌بندی فضای نامی معمولا برای پکیج‌هایی کاربرد دارد که چندین زیرپکیج که ارتباطی با هم ندارند را شامل می‌شود و یا شرکت‌های که کتابخانه‌هایی زیادی را برای محصولات خود توزیع می‌کنند. بنابراین این نوع از ساختار بسته‌بندی مناسب هر پکیجی نیست.

ایجاد پکیج فضای نامی

در حال حاضر 3 رویکرد کلی برای ساخت این نوع از بسته بندی در پایتون وجود دارد:

  1. بسته‌بندی Native: این نوع از بسته‌بندی در نسخه 3.3 به بعد پایتون ارائه شد و تنها زمانی که از نسخه 3 پایتون استفاده و با pip پکیج‌ها را نصب می‌کنید توصیه می‌شود.
  2. pkgutil: این نوع از بسته‌بندی نیز زمانی که بخواهید بسته‌های شما هر دو نسخه 2 و 3 پایتون را پشتیبانی کنند و با هر دو pip و python setup.py install نصب شوند، توصیه می‌شود.
  3. pkg_resources-style: این روش نیز تنها زمانی که شما لازم دارید بسته شما با بسته‌های دیگری که از همین روش استفاده کردند سازگاری داشته باشد توصیه می‌شود. البته به یه یاد داشته باشید که این نوع از بسته‌بندی با دو بسته‌بندی فوق سازگار نیست.

1. بسته‌بندی Native

در این نوع از بسته‌بندی تنها کاری که باید انجام دهید این هست که init__.pyــ  را از مسیر اصلی حذف کنید:

mynamespace/
	subpkg_a/
		__init__.py
    	module.py
    	
	subpkg_b/
		__init__.py
    	module.py
    	
setup.py

از آنجایی که mynamespace شامل فایل init__.pyــ نیست، تابع find_packages در setup.py قادر نخواهد بود زیر پکیج‌های شما را شناسایی کند از اینرو باید بجای آن از تابع find_namespace_packages یا بصورت مشخص لیست تمام پکیج‌های خود را وارد کنید:

from setuptools import setup, find_namespace_packages

setup(
    name='mynamespace-subpackage-a',
    ...
    packages=find_namespace_packages(include=['mynamespace.*'])
)

همانطور که گفته شد این نوع از بسته‌بندی از نسخه 3.3 به بعد پایتون اضافه شده است. اگر از این روش در پکیج‌های خود استفاده می‌کنید، پکیج شما برای توسعه دهندگانی که از ورژن 2 پایتون استفاده می‌کنند قابل استفاده نخواهد بود. 

2. بسته‌بندی pkgutil

به همراه انتشار پایتون 2.3 ماژول pkgutil ارائه شد که با کمک آن می‌توانید پکیج خود را به نحوه معرفی کنید که که با پایتون نسخه 2.3+ و 3 سازگار باشد. معمولا این روش بعلت سازگاری بالا توصیه می‌شود.

برای اینکه پکیج خود را با این روش ایجاد کنید لازم است فایل init__.pyــ را در مسیر اصلی تعریف کنید:

mynamespace/
	__init__.py
	
	subpkg_a/
		__init__.py
    	module.py
    	
	subpkg_b/
		__init__.py
    	module.py
    	
setup.py

فایل init__.pyــ در مسیر اصلی پکیج باید شامل دستور زیر باشد:

__path__ = __import__('pkgutil').extend_path(__path__, __name__)

3. بسته‌بندی pkg_resources-style

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

برای اینکه از این روش استفاده کنید شما لازم دارید که فایل init__.pyــ را در مسیر اصلی بصورت زیر تعریف کنید:

__import__('pkg_resources').declare_namespace(__name__)

در نهایت باید در فایل setup پارامتر namespace_packages را به شیوه زیر مقداردهی کنید:

from setuptools import find_packages, setup

setup(
    name='mynamespace',
    ...
    packages=find_packages()
    namespace_packages=['mynamespace']
)