Прикључци у Пајтону

Prof. dr Igor Dejanović (igord at uns ac rs)

Kreirano 2023-11-08 Wed 11:37, pritisni ESC za mapu, m za meni, Ctrl+Shift+F za pretragu

Sadržaj

1. Мотивација

  • Пакет жели да омогући команду која се може позвати са терминала.
  • Пакет жели да омогући покретање GUI апликације.
  • Пакет жели да омогући прилагођавање своје функционалности употребом прикључака (plugins).

2. Конзолне скрипте

2.1. Организација пројекта

project_root_directory
├── pyproject.toml        # and/or setup.cfg, setup.py
└── src
    └── timmins
        ├── __init__.py
        └── ...

У __init__.py:

def hello_world():
    print("Hello world")

2.2. Покретање као модул

У src/timmins/__main__.py:

from . import hello_world

if __name__ == '__main__':
    hello_world()

Сада је могуће покренути функцију са терминала на следећи начин:

$ python -m timmins
Hello world

2.3. Преко екстензионе тачке

У pyproject.toml:

[project.scripts]
hello-world = "timmins:hello_world"

Сада можемо скрипту директно позвати:

$ hello-world
Hello world

2.4. Исто тако можемо регистровати и GUI скрипту

[project.gui-scripts]
hello-world = "timmins:hello_world"

2.5. Генеричко решење

  • Регистрација конзолних и GUI скрипти представља специјалну употребу општег механизма за проширење који називамо Прикључне тачке.

3. Прикључне тачке (Entry Points)

  • Општи начин проширења функционалности пакета.
  • Тип метаподатка који пакети могу да региструју.
  • Други пакети могу динамички да открију и исчитају ове метаподатке.

3.1. Организација пројекта

Крећемо од исте организације пројекта.

project_root_directory
├── pyproject.toml        # and/or setup.cfg, setup.py
└── src
    └── timmins
        ├── __init__.py
        └── ...

У __init__.py:

def hello_world():
    print("Hello world")

3.2. Поставка проблема

  • Желимо да омогућимо прилагођен приказ основног стринга.
  • Прилагођавање могу да ураде други пакети.

    На пример испис може бити промењен на следећи начин:

      !!! Hello world !!!
    

3.3. Први корак

Раздвајање приказа на две функције:

def display(text):
    print(text)

def hello_world():
    display('Hello world')

3.4. Откривање прикључака и модификоване display функције

from importlib.metadata import entry_points
display_eps = entry_points(group='timmins.display')

Прикључне тачке имају назив, групу и вредност (атрибути name, group, value):

(
    EntryPoint(name='excl', value='timmins_plugin_fancy:excl_display', group='timmins.display'),
    ...,
)

3.5. Учитавање објекта регистрованог у прикључној тачки

display = display_eps[0].load()

Потребно је дефинисати понашање у случају да не постоји пакет који региструје функцију у прикљуној тачки:

from importlib.metadata import entry_points
display_eps = entry_points(group='timmins.display')
try:
    display = display_eps[0].load()
except IndexError:
    def display(text):
        print(text)

def hello_world():
    display('Hello world')

3.6. Регистрација прилагођене display функције

timmins-plugin-fancy
├── pyproject.toml        # and/or setup.cfg, setup.py
└── src
    └── timmins_plugin_fancy
        └── __init__.py

У src/timmins_plugin_fancy/__init__.py:

def excl_display(text):
    print('!!!', text, '!!!')

pyproject.toml

# Note the quotes around timmins.display in order to escape the dot .
[project.entry-points."timmins.display"]
excl = "timmins_plugin_fancy:excl_display"

Сада можемо видети дејство прикључка уколико је пакет инсталиран:

>>> from timmins import hello_world

>>> hello_world()
!!! Hello world !!!

У случају да пакет timmins_plugin_fancy није инсталиран понашање је подразумевано:

>>> from timmins import hello_world

>>> hello_world()
Hello world

3.7. Регистрација више прикључака под истом групом

  • Желимо да имамо више различитих начина исписа:
def excl_display(text):
    print('!!!', text, '!!!')

def lined_display(text):
    print(''.join(['-' for _ in text]))
    print(text)
    print(''.join(['-' for _ in text]))

pyproject.toml

[project.entry-points."timmins.display"]
excl = "timmins_plugin_fancy:excl_display"
lined = "timmins_plugin_fancy:lined_display"

Сада можемо у базном пакету урадити нешто попут овога:

display_eps = entry_points(group='timmins.display')
try:
    display = display_eps['lined'].load()
except KeyError:
    # if the 'lined' display is not available, use something else
    ...

Или можемо учитати све прикључке:

display_eps = entry_points(group='timmins.display')
for ep in display_eps:
    display = ep.load()
    # do something with display
    ...

4. Литература