importlib.metadata -- دسترسی به فرادادهی بسته¶
اضافه شده در نسخهی 3.8.
تغییر یافته در نسخهی 3.10: importlib.metadata دیگر موقتی نیست.
کد منبع: Lib/importlib/metadata/__init__.py
importlib.metadata کتابخانهای است که دسترسی به فرادادهی یک بسته توزیعی نصبشده را فراهم میکند، مانند نقاط ورود آن یا نامهای سطح بالای آن (بستههای ایمپورت، ماژولها، در صورت وجود). این کتابخانه که تا حدی بر پایهی سیستم ایمپورت پایتون ساخته شده است، APIهای نقطهی ورود و فراداده را فراهم میکند که پیشتر توسط بستهی اکنون حذفشدهی pkg_resources ارائه میشدند. این کتابخانه به همراه importlib.resources، جایگزین pkg_resources شده است.
importlib.metadata روی بستههای توزیع شخص ثالث نصبشده از طریق ابزارهایی مانند pip در پوشهی site-packages پایتون عمل میکند. بهطور مشخص، با توزیعهایی کار میکند که دارای پوشههای dist-info یا egg-info قابلکشف هستند، و نیز با فرادادهای که در مشخصات فرادادهی اصلی تعریفشده است.
مهم
این موارد لزوماً معادل یا متناظر یکبهیک با نامهای بسته ایمپورت سطح بالا که میتوان آنها را در کد پایتون ایمپورت کرد، نیستند. یک بسته توزیع میتواند شامل چندین بسته ایمپورت (و ماژولهای منفرد) باشد، و یک بسته ایمپورت سطح بالا ممکن است در صورتی که یک بسته فضای نام باشد، به چندین بسته توزیع نگاشت شود. میتوانید از packages_distributions() برای دریافت نگاشت بین آنها استفاده کنید.
بهطور پیشفرض، فرادادهی توزیع میتواند در سامانه فایلبندی یا در آرشیوهای zip موجود در sys.path قرار داشته باشد. از طریق یک سازوکار توسعه، فراداده میتواند تقریباً در هر جایی قرار داشته باشد.
همچنین ببینید
- https://importlib-metadata.readthedocs.io/
مستندات
importlib_metadata، که یک بکپورت ازimportlib.metadataرا فراهم میکند. این مستندات شامل یک مرجع API برای کلاسها و توابع این ماژول، و همچنین یک راهنمای مهاجرت برای کاربران فعلیpkg_resourcesاست.
نمای کلی¶
فرض کنید میخواهید رشته نسخه را برای یک بسته توزیع (Distribution Package) که با استفاده از pip نصب کردهاید، دریافت کنید. ما با ایجاد یک محیط مجازی و نصب چیزی در آن شروع میکنیم:
$ python -m venv example
$ source example/bin/activate
(example) $ python -m pip install wheel
میتوانید رشتهی نسخهی wheel را با اجرای دستور زیر به دست آورید:
(example) $ python
>>> from importlib.metadata import version
>>> version('wheel')
'0.32.3'
همچنین میتوانید مجموعهای از نقطههای ورودی را دریافت کنید که بر اساس ویژگیهای EntryPoint (معمولاً 'group' یا 'name') قابل انتخاب هستند، مانند console_scripts، distutils.commands و دیگر موارد. هر گروه شامل مجموعهای از اشیای EntryPoint است.
میتوانید فراداده یک توزیع را دریافت کنید:
>>> from importlib.metadata import metadata
>>> list(metadata('wheel'))
['Metadata-Version', 'Name', 'Version', 'Summary', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', 'Project-URL', 'Project-URL', 'Project-URL', 'Keywords', 'Platform', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Classifier', 'Requires-Python', 'Provides-Extra', 'Requires-Dist', 'Requires-Dist']
همچنین میتوانید شماره نسخه توزیع را دریافت کنید، پروندههای تشکیلدهنده آن را فهرست کنید و فهرستی از نیازمندیهای توزیع توزیع را به دست آورید.
- exception importlib.metadata.PackageNotFoundError¶
زیرکلاسی از
ModuleNotFoundErrorکه توسط چندین تابع در این ماژول، هنگام پرسوجو برای بسته توزیعی که در محیط پایتون جاری نصب نیست، پرتاب میشود.
- exception importlib.metadata.MetadataNotFound¶
زیرکلاسی از
FileNotFoundErrorکه هنگام تلاش برای بارگذاری فراداده از یک پوشهی توزیع که خالی است یا به شکل دیگری پروندهی فرادادهای را در خود ندارد، بالا برده میشود.
API تابعی¶
این بسته، قابلیتهای زیر را از طریق API عمومی خود فراهم میکند.
نقاط ورود¶
- importlib.metadata.entry_points(**select_params)¶
نمونهای از
EntryPointsرا برمیگرداند که نقاط ورود محیط جاری را توصیف میکند. هر پارامتر کلیدواژهای که داده شود، به متدselect()ارسال میشود تا با ویژگیهای تعریفهای جداگانهی نقاط ورود مقایسه شود.نکته: برای پرسوجو دربارهی نقطههای ورود بر اساس ویژگی
EntryPoint.dist، بهجای آن ازDistribution.entry_points()استفاده کنید (زیرا نمونههای مختلفDistributionدر حال حاضر با یکدیگر مقایسهی مساوی ندارند، حتی اگر ویژگیهای یکسانی داشته باشند)
- class importlib.metadata.EntryPoints¶
جزئیات مجموعهای از نقاط ورودی نصبشده (entry points).
همچنین یک ویژگی
.groupsارائه میدهد که تمام گروههای شناساییشدهی نقطه ورود (entry point) را گزارش میدهد، و یک ویژگی.namesکه تمام نامهای شناساییشدهی نقطه ورود را گزارش میدهد.
- class importlib.metadata.EntryPoint¶
جزئیات یک نقطه ورود نصبشده.
هر نمونهی
EntryPointدارای ویژگیهای.name،.groupو.valueو یک متد.load()برای حل کردن مقدار است. همچنین ویژگیهای.module،.attrو.extrasبرای دریافت کامپوننتهای ویژگی.valueو نیز.distبرای به دست آوردن اطلاعات مربوط به بستهی توزیعی که نقطه ورود را فراهم میکند، وجود دارند.
پرسوجوی همهی نقطههای ورود:
>>> eps = entry_points()
تابع entry_points() یک شیء EntryPoints را برمیگرداند، مجموعهای از تمام اشیای EntryPoint که برای سهولت دارای ویژگیهای names و groups است:
>>> sorted(eps.groups)
['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']
EntryPoints یک متد select() برای انتخاب نقاط ورود منطبق بر ویژگیهای مشخص دارد. نقاط ورود را در گروه console_scripts انتخاب کنید:
>>> scripts = eps.select(group='console_scripts')
بهطور معادل، از آنجا که entry_points() آرگومانهای کلیدواژهای را به select منتقل میکند:
>>> scripts = entry_points(group='console_scripts')
یک اسکریپت مشخص به نام "wheel" (موجود در پروژه wheel) را انتخاب کنید:
>>> 'wheel' in scripts.names
True
>>> wheel = scripts['wheel']
معادل آن، در هنگام انتخاب، آن نقطه ورود را پرسوجو کنید:
>>> (wheel,) = entry_points(group='console_scripts', name='wheel')
>>> (wheel,) = entry_points().select(group='console_scripts', name='wheel')
نقطه ورود تعیینشده را بررسی کنید:
>>> wheel
EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')
>>> wheel.module
'wheel.cli'
>>> wheel.attr
'main'
>>> wheel.extras
[]
>>> main = wheel.load()
>>> main
<function main at 0x103528488>
group و name مقادیر دلخواهی هستند که نویسنده بسته آنها را تعریف کرده است و معمولاً یک کلاینت مایل است تمام نقطههای ورود یک گروه خاص را حل کند. برای اطلاعات بیشتر درباره نقطههای ورود، تعریف و کاربرد آنها، مستندات setuptools را بخوانید.
تغییر یافته در نسخهی 3.12: نقاط ورودی «قابلانتخاب» در importlib_metadata 3.6 و پایتون 3.10 معرفی شدند. پیش از آن تغییرات، entry_points هیچ پارامتری را نمیپذیرفت و همیشه یک دیکشنری از نقاط ورودی را برمیگرداند که بر اساس گروه کلیدگذاری شده بود. با importlib_metadata 5.0 و پایتون 3.12، entry_points همیشه یک شیء EntryPoints را برمیگرداند. برای گزینههای سازگاری، backports.entry_points_selectable را ببینید.
تغییر یافته در نسخهی 3.13: اشیای EntryPoint دیگر رابطی شبیه به تاپل ارائه نمیدهند (__getitem__()).
فرادادهی توزیع¶
- importlib.metadata.metadata(distribution_name)¶
فراداده توزیع مربوط به بسته توزیع نامبردهشده را بهعنوان یک نمونه از
PackageMetadataبرمیگرداند.اگر بسته توزیع نامبرده در محیط پایتون جاری نصب نشده باشد،
PackageNotFoundErrorرا پرتاب میکند.اگر یک بستهی توزیع موجود باشد اما هیچ پروندهی METADATAی وجود نداشته باشد، استثنای
MetadataNotFoundرا بالا میآورد.
- class importlib.metadata.PackageMetadata¶
یک پیادهسازی عینی از پروتکل PackageMetadata.
علاوه بر ارائه متدها و ویژگیهای تعریفشدهی پروتکل، اندیسگذاری نمونه معادل فراخوانی متد
get()است.
هر بسته توزیع شامل فرادادهای است که میتوانید آن را با استفاده از تابع metadata() استخراج کنید:
>>> wheel_metadata = metadata('wheel')
کلیدهای ساختار دادهی بازگرداندهشده، نام کلیدواژههای فراداده هستند و مقادیر بهصورت تجزیهنشده از فرادادهی توزیع بازگردانده میشوند:
>>> wheel_metadata['Requires-Python']
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
PackageMetadata همچنین ویژگی json را ارائه میدهد که تمام فرادادهها را در قالبی سازگار با JSON مطابق PEP 566 برمیگرداند:
>>> wheel_metadata.json['requires_python']
'>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*'
مجموعهی کامل فرادادههای موجود در اینجا توصیف نشده است. برای جزئیات بیشتر، Core metadata specification در PyPA را ببینید.
تغییر یافته در نسخهی 3.15: پیشتر و بر حسب تصادف، اگر یک پروندهی METADATA از یک توزیع مفقود بود، یک شیء خالی PackageMetadata بازگردانده میشد که از یک پروندهی METADATA خالی قابل تشخیص نبود. اکنون، مفقود بودن پروندهی METADATA باعث ایجاد استثنای MetadataNotFound میشود.
تغییر یافته در نسخهی 3.10: Description اکنون هنگام ارائه از طریق بار در فراداده گنجانده شده است. نویسههای ادامهی خط حذف شدهاند.
ویژگی json افزوده شد.
نسخههای توزیع¶
- importlib.metadata.version(distribution_name)¶
نسخه بسته توزیع نصبشده را برای بسته توزیع نامبرده برمیگرداند.
اگر بسته توزیع نامبرده در محیط پایتون جاری نصب نشده باشد،
PackageNotFoundErrorرا پرتاب میکند.
تابع version() سریعترین راه برای دریافت شمارهی نسخهی یک بسته توزیع بهصورت یک رشته است:
>>> version('wheel')
'0.32.3'
پروندههای توزیع¶
- importlib.metadata.files(distribution_name)¶
مجموعه کامل پروندههای موجود در بستهی توزیع نامبرده را به شکل نمونههای
PackagePathبرمیگرداند.اگر بسته توزیع نامبرده در محیط پایتون جاری نصب نشده باشد،
PackageNotFoundErrorرا پرتاب میکند.اگر توزیع یافت شود اما رکوردهای پایگاهدادهی نصب که پروندههای مرتبط با بسته توزیع را گزارش میدهند موجود نباشند، مقدار
Noneرا برمیگرداند.
- class importlib.metadata.PackagePath¶
یک شیء مشتقشده از
pathlib.PurePathبا ویژگیهای اضافیdist،sizeوhashکه با فرادادهی نصب بستهی توزیع برای آن پرونده مطابقت دارند، و همچنین:- locate()¶
در صورت امکان،
SimplePathملموس را که امکان دسترسی به دادهها را فراهم میکند بازگردانید، در غیر این صورت استثنایNotImplementedErrorرا بالا بیاورید.
- class importlib.metadata.SimplePath¶
پروتکلی که زیرمجموعهی حداقلی از
pathlib.Pathرا نشان میدهد که امکان بررسی وجود آن باexists()، پیمایش با استفاده ازjoinpath()وparent، و بازیابی دادهها با استفاده ازread_text()وread_bytes()را فراهم میکند.
تابع files() نام یک Distribution Package را میگیرد و تمام پروندههای نصبشده توسط این توزیع را برمیگرداند. برای مثال:
>>> util = [p for p in files('wheel') if 'util.py' in str(p)][0]
>>> util
PackagePath('wheel/util.py')
>>> util.size
859
>>> util.dist
<importlib.metadata._hooks.PathDistribution object at 0x101e0cef0>
>>> util.hash
<FileHash mode: sha256 value: bYkw5oMccfazVCoYQwKkkemoVyMAFoR34mmKBx8R1NI>
پس از اینکه پرونده را داشتید، میتوانید محتوای آن را نیز بخوانید:
>>> print(util.read_text())
import base64
import sys
...
def as_bytes(s):
if isinstance(s, text_type):
return s.encode('utf-8')
return s
همچنین میتوانید از متد locate() برای دریافت مسیر مطلق پرونده استفاده کنید:
>>> util.locate()
PosixPath('/home/gustav/example/lib/site-packages/wheel/util.py')
در صورتی که پرونده فرادادهای که پروندهها را فهرست میکند (RECORD یا SOURCES.txt) موجود نباشد، files() مقدار None را برمیگرداند. اگر معلوم نباشد که توزیع هدف دارای فراداده است، فراخواننده ممکن است بخواهد فراخوانیهای files() را در always_iterable قرار دهد یا به روش دیگری در برابر این وضعیت محافظت کند.
نیازمندیهای توزیع¶
- importlib.metadata.requires(distribution_name)¶
مشخصکنندههای وابستگی اعلامشده برای بسته توزیع نامبرده را برمیگرداند.
اگر بسته توزیع نامبرده در محیط پایتون جاری نصب نشده باشد،
PackageNotFoundErrorرا پرتاب میکند.
برای دریافت مجموعه کامل نیازمندیهای یک بسته توزیع، از تابع requires() استفاده کنید:
>>> requires('wheel')
["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
نگاشت ایمپورت به بستههای توزیع¶
- importlib.metadata.packages_distributions()¶
نگاشتی از نام ماژولهای سطح بالا و بستههای ایمپورت که از طریق
sys.meta_pathیافت میشوند، به نام بستههای توزیع (در صورت وجود) که پروندههای متناظر را فراهم میکنند، برمیگرداند.برای پشتیبانی از بستههای فضای نام (که ممکن است اعضای آنها توسط چندین بسته توزیع فراهم شده باشند)، هر نام ایمپورت سطح بالا به فهرستی از نامهای توزیع نگاشت میشود، نه اینکه بهطور مستقیم به یک نام واحد نگاشت شود.
متدی آسانکننده برای تعیین نام بسته توزیع (یا نامها، در مورد یک بسته فضای نام) که هر ماژول پایتون سطحبالای قابل ایمپورت یا بسته ایمپورت را فراهم میکنند:
>>> packages_distributions()
{'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...}
برخی نصبهای قابلویرایش، نامهای سطح بالا را ارائه نمیدهند، و بنابراین این تابع با چنین نصبهایی قابلاتکا نیست.
اضافه شده در نسخهی 3.10.
توزیعها¶
در حالی که API سطح ماژول که در بالا توصیف شد رایجترین و راحتترین روش استفاده است، تمام این اطلاعات از طریق کلاس Distribution قابل دسترسی هستند. Distribution یک شیء انتزاعی است که فرادادهی مربوط به یک Distribution Package پایتون را بازنمایی میکند. با فراخوانی تابع distribution()، نمونهی زیرکلاس مشخص Distribution را برای یک بستهی توزیع نصبشده دریافت کنید:
>>> from importlib.metadata import distribution
>>> dist = distribution('wheel')
>>> type(dist)
<class 'importlib.metadata.PathDistribution'>
- importlib.metadata.distribution(distribution_name)¶
نمونهای از
Distributionبرمیگرداند که بسته توزیع نامبردهشده را توصیف میکند.اگر بسته توزیع نامبرده در محیط پایتون جاری نصب نشده باشد،
PackageNotFoundErrorرا پرتاب میکند.
بنابراین، یک روش جایگزین برای دریافت مثلاً شمارهی نسخه، استفاده از ویژگی Distribution.version است:
>>> dist.version
'0.32.3'
همین امر برای entry_points() و files() نیز صدق میکند.
- class importlib.metadata.Distribution¶
جزئیات یک بسته توزیع نصبشده.
توجه: نمونههای متفاوت
Distributionدر حال حاضر هنگام مقایسه برابر در نظر گرفته نمیشوند، حتی اگر به همان توزیع نصبشده مربوط باشند و بنابراین ویژگیهای یکسانی داشته باشند.- static at(path)¶
- classmethod from_name(name)¶
یک نمونهی
Distributionرا در مسیر دادهشده یا با نام دادهشده برمیگرداند.
- classmethod discover(*, context=None, **kwargs)¶
یک پیمایشپذیر از نمونههای
Distributionرا برای تمام بستهها برمیگرداند (نگاه کنید به distribution-discovery).آرگومان اختیاری context یک نمونه از
DistributionFinder.Contextاست که برای اصلاح جستجوی توزیعها استفاده میشود. به عنوان جایگزین، kwargs ممکن است حاوی آرگومانهای کلیدواژهای برای ساخت یکDistributionFinder.Contextجدید باشد.
- metadata: PackageMetadata¶
اگر پروندهی METADATA در بسته موجود نباشد، استثنای
MetadataNotFoundرا بالا میآورد.انواع و اقسام فرادادههای اضافی به شکل یک نمونهی
PackageMetadataروی نمونههایDistributionدر دسترس هستند:>>> dist.metadata['Requires-Python'] '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' >>> dist.metadata['License'] 'MIT'
مجموعهی کامل فرادادههای موجود در اینجا توصیف نشده است. برای جزئیات بیشتر، Core metadata specification در PyPA را ببینید.
- version: str¶
چند فیلد فرادادهی دیگر نیز بهعنوان ویژگیهای میانبر در دسترس هستند.
اضافه شده در نسخهی 3.10: میانبر
nameاضافه شد.
- origin¶
برای بستههای قابل ویرایش، یک ویژگی
originممکن است فرادادهی PEP 610 را نمایش دهد (برای بستههای غیرقابل ویرایش،originبرابر باNoneاست):>>> dist.origin.url 'file:///path/to/wheel-0.32.3.editable-py3-none-any.whl'
شیء
originاز ساختار دادهی Direct URL Data Structure پیروی میکند.اضافه شده در نسخهی 3.13.
- entry_points: EntryPoints¶
نقطههای ورودی ارائه شده توسط این بستهی توزیع.
- files: list[PackagePath] | None¶
تمام پروندههای موجود در این بستهی توزیع. درست مانند
files()، اگر رکوردی وجود نداشته باشد، این تابعNoneرا برمیگرداند.
هنگام پیادهسازی implementing-custom-providers، دو متد انتزاعی زیر باید پیادهسازی شوند:
- locate_file(path)¶
مشابه
PackagePath.locate()، یکSimplePathبرای مسیر دادهشده برمیگرداند. یکos.PathLikeیا یکstrمیپذیرد.
- read_text(filename)¶
یک میانبر برای
distribution.locate_file(filename).read_text().
کشف توزیع¶
بهطور پیشفرض، این بسته پشتیبانی توکاری برای کشف فرادادهی بستههای توزیع در سامانه فایلبندی و پروندههای zip فراهم میکند. جستوجوی این یابندهی فراداده بهطور پیشفرض از sys.path استفاده میکند، اما تفسیر آن از این مقادیر، تفاوت اندکی با تفسیر سایر سازوکارهای ایمپورت دارد. بهویژه:
importlib.metadataاشیایbytesموجود درsys.pathرا به رسمیت نمیشناسد.importlib.metadataبهطور اتفاقی اشیایpathlib.Pathموجود درsys.pathرا معتبر میشمارد، حتی اگر چنین مقادیری برای ایمپورتها نادیده گرفته شوند.
- class importlib.metadata.DistributionFinder¶
یک زیرکلاس از
MetaPathFinderکه قادر به کشف توزیعهای نصبشده است.ارائهدهندگان سفارشی باید این رابط را برای ارائهی فراداده پیادهسازی کنند.
- class Context(**kwargs)¶
یک
Contextبه ارائهدهندهی سفارشی ابزاری میدهد تا جزئیات اضافی را فراتر از.nameو.pathهنگام جستوجوی توزیعها، از فراخوانندگان توابع کشف توزیع مانندdistributions()یاDistribution.discover()درخواست کند.برای مثال، یک ارائهدهنده میتواند مجموعههایی از بستهها را در یک
realm«عمومی» یا «خصوصی» ارائه دهد. ممکن است فراخوانندهی توابع کشف توزیع بخواهد فقط برای توزیعها در یک قلمرو خاص پرسوجو کند و میتواندdistributions(realm="private")را فراخوانی کند تا به ارائهدهندهی سفارشی سیگنال دهد که فقط توزیعهای آن قلمرو را لحاظ کند.هر
DistributionFinderباید هر پارامتری را انتظار داشته باشد و در صورت لزوم باید تلاش کند تا پارامترهای کانونیکال تعریفشده در زیر را رعایت کند.برای جزئیات بیشتر، بخش مربوط به پیادهسازی فراهمکنندگان سفارشی را ببینید.
- name¶
نام خاصی که یابندهی توزیع باید با آن مطابقت داشته باشد.
مقدار
Noneبرای.nameبا تمام توزیعها مطابقت دارد.
- importlib.metadata.distributions(**kwargs)¶
یک شیء قابل پیمایش از نمونههای
Distributionبرای همهی بستهها برمیگرداند.آرگومان kwargs ممکن است حاوی یک آرگومان کلیدواژهای
context، یک نمونه ازDistributionFinder.Contextباشد، یا آرگومانهای کلیدواژهای را برای ساختن یکDistributionFinder.Contextجدید ارسال کند.DistributionFinder.Contextبرای اصلاح جستوجوی توزیعها استفاده میشود.
پیادهسازی فراهمکنندگان سفارشی¶
importlib.metadata دو سطح API را پوشش میدهد: یکی برای مصرفکنندگان و دیگری برای ارائهدهندگان. بیشتر کاربران مصرفکننده هستند و فرادادهی ارائهشده توسط بستهها را مصرف میکنند. با این حال، موارد استفاده دیگری نیز وجود دارند که در آنها کاربران میخواهند فراداده را از طریق سازوکار دیگری، مثلاً در کنار یک واردکننده سفارشی، در معرض قرار دهند. چنین مورد استفادهای به یک ارائهدهنده سفارشی نیاز دارد.
از آنجا که فرادادهی بسته توزیعی از طریق جستوجوهای sys.path یا بهطور مستقیم از طریق بارگذارهای بسته در دسترس نیست، فرادادهی یک توزیع از طریق یابندهها در سیستم ایمپورت یافته میشود. برای یافتن فرادادهی یک بسته توزیعی، importlib.metadata فهرست یابندههای فرامسیر در sys.meta_path را جستوجو میکند.
این پیادهسازی دارای قلابهایی (hooks) است که در PathFinder ادغام شدهاند و فراداده بستههای توزیع یافتشده در سامانه پرونده را ارائه میکنند.
کلاس انتزاعی importlib.abc.MetaPathFinder رابطی را تعریف میکند که سیستم ایمپورت پایتون از یابندهها انتظار دارد. importlib.metadata این پروتکل را با جستوجوی یک فراخوانیپذیر اختیاری find_distributions در یابندههای sys.meta_path توسعه میدهد و این رابط توسعهیافته را به عنوان کلاس پایه انتزاعی DistributionFinder ارائه میکند که این متد انتزاعی را تعریف میکند:
@abc.abstractmethod
def find_distributions(context=DistributionFinder.Context()) -> Iterable[Distribution]:
"""پیمایشپذیری از همه نمونههای Distribution برمیگرداند که توانایی بارگذاری فراداده بستهها برای ``context`` مشخصشده را دارند.
"""
شیء DistributionFinder.Context ویژگیهای path و name را که نشاندهندهی مسیر جستوجو و نام برای تطبیق هستند فراهم میکند و ممکن است زمینهی مرتبط دیگری را که مصرفکننده به دنبال آن است تأمین کند.
در عمل، برای پشتیبانی از یافتن متادیتای بستهی توزیع در مکانهایی غیر از سیستم پرونده، از Distribution زیرکلاسسازی کرده و متدهای انتزاعی را پیادهسازی کنید. سپس از یک یابندهی سفارشی، نمونههایی از این Distribution مشتقشده را در متد find_distributions() برگردانید.
مثال¶
یک یابنده سفارشی را تصور کنید که ماژولهای پایتون را از یک پایگاه داده بارگذاری میکند:
class DatabaseImporter(importlib.abc.MetaPathFinder):
def __init__(self, db):
self.db = db
def find_spec(self, fullname, target=None) -> ModuleSpec:
return self.db.spec_from_name(fullname)
sys.meta_path.append(DatabaseImporter(connect_db(...)))
آن ایمپورتکننده اکنون احتمالاً ماژولهای قابل ایمپورت را از یک پایگاه داده فراهم میکند، اما هیچ متادیتا یا نقطهی ورودیای ارائه نمیدهد. برای اینکه این ایمپورتکنندهی سفارشی متادیتا فراهم کند، باید DistributionFinder را نیز پیادهسازی کند:
from importlib.metadata import DistributionFinder
class DatabaseImporter(DistributionFinder):
...
def find_distributions(self, context=DistributionFinder.Context()):
query = dict(name=context.name) if context.name else {}
for dist_record in self.db.query_distributions(query):
yield DatabaseDistribution(dist_record)
به این ترتیب، query_distributions رکوردهایی را برای هر توزیع ارائهشده توسط پایگاه داده که با پرسوجو مطابقت دارد، بازمیگرداند. برای مثال، اگر requests-1.0 در پایگاه داده باشد، find_distributions یک DatabaseDistribution برای Context(name='requests') یا Context(name=None) تولید میکند.
برای سادگی، این مثال context.path را نادیده میگیرد. ویژگی path بهطور پیشفرض برابر با sys.path است و مجموعهای از مسیرهای ایمپورت است که در جستجو در نظر گرفته میشوند. یک DatabaseImporter بهطور بالقوه میتواند بدون هیچ توجهی به مسیر جستجو کار کند. با فرض اینکه ایمپورتکننده هیچ بخشبندی انجام نمیدهد، «path» بیارتباط خواهد بود. برای نشان دادن هدف از path، مثال باید یک DatabaseImporter پیچیدهتر را نشان دهد که رفتار آن بسته به sys.path/PYTHONPATH تغییر میکند. در آن صورت، find_distributions باید context.path را رعایت کند و فقط نمونههای Distribution مرتبط با آن مسیر را برگرداند.
در این صورت، DatabaseDistribution چیزی شبیه به این خواهد بود:
class DatabaseDistribution(importlib.metadata.Distribution):
def __init__(self, record):
self.record = record
def read_text(self, filename):
"""
Read a file like "METADATA" for the current distribution.
"""
if filename == "METADATA":
return f"""Name: {self.record.name}
Version: {self.record.version}
"""
if filename == "entry_points.txt":
return "\n".join(
f"""[{ep.group}]\n{ep.name}={ep.value}"""
for ep in self.record.entry_points)
def locate_file(self, path):
raise RuntimeError("This distribution has no file system")
این پیادهسازی اولیه باید فراداده و نقاط ورود را برای بستههای ارائهشده توسط DatabaseImporter فراهم کند، با فرض اینکه record ویژگیهای مناسب .name، .version و .entry_points را فراهم کند.
کلاس DatabaseDistribution همچنین ممکن است پروندههای متادیتای دیگری مانند RECORD (مورد نیاز برای Distribution.files) را فراهم کند یا پیادهسازی Distribution.files را بازنویسی (override) کند. برای الهام گرفتن بیشتر، سورس را ببینید.