2016-11-09 38 views
14

Tôi đang phát triển một bot slack với các plugin sử dụng các điểm vào. Tôi muốn tự động thêm plugin trong thời gian chạy.Cách tự động thêm và tải điểm vào?

Tôi có một dự án với cấu trúc này:

+ ~/my_project_dir/ 
    + my_projects_python_code/ 
    + plugins/ 
     - plugin1.py 
     - plugin2.py 
     - ... 
     - pluginN.py 
    - setup.py 
    - venv/ 
    - install.sh 

tập tin setup.py của tôi trông như thế này:

from setuptools import setup, find_packages 

setup(
    name="My_Project_plugins", 
    version="1.0", 
    packages=['plugins'], 
    entry_points=""" 
     [my_project.plugins] 
     plugin1 = plugins.plugin1:plugin1_class 
     plugin2 = plugins.plugin2:plugin2_class 
     ... 
     pluginN = plugins.pluginN:pluginN_class 
    """ 
     ) 

Chạy sudo install.sh nào sau đây:

  1. Sao chép tập tin cần thiết đến /usr/share/my_project_dir/

  2. Kích hoạt virtualenv tại /usr/share/my_project_dir/venv/bin/activate

  3. Run: python setup.py develop

này hoạt động như mong đợi và thiết lập các điểm vào tôi một cách chính xác để tôi có thể sử dụng chúng thông qua các bot.

Nhưng tôi muốn có thể thêm plugin vào setup.py và có thể sử dụng plugin trong khi bot đang chạy. Vì vậy, tôi muốn thêm một dòng: pluginN+1 = plugins.pluginN+1:pluginN+1_class và có pluginN + 1 có sẵn để sử dụng.

Những gì tôi đã cố gắng/học:

  • Sau /usr/share/my_project_dir/venv/bin/activate tôi mở một shell tương tác Python và lặp qua pkg_resources.iter_entry_points(), trong đó liệt kê tất cả những gì đã được nạp từ trạng thái ban đầu của setup.py (tức plugin1 qua pluginN)

  • Nếu tôi thêm một dòng vào setup.py và chạy sudo python setup.py develop và lặp lại với cùng một vỏ Python, nó không nhận plugin mới nhưng nếu tôi thoát vỏ và mở lại, plugin mới được chọn lên.

  • tôi nhận thấy rằng khi tôi cài đặt bot, một phần của đầu ra nói:

    • Copying My_Project_plugins-1.0-py2.7.egg to /usr/share/my_project-dir/venv/lib/python2.7/site-packages
  • Khi tôi cd /usr/share/my_project_dir/, kích hoạt virtualenv của tôi, và chạy setup.py từ vỏ nó nói :

    • Creating /usr/local/lib/python2.7/dist-packages/My_Project-plugins.egg-link (link to .) My_Project-plugins 1.0 is already the active version in easy-install.pth
+0

Nếu bạn có nhiều plugin cho một chương trình phổ biến, hãy thực hiện một số thao tác như 'pip install',' pip uninstall', 'pip search' là lựa chọn không mong muốn? –

+0

Mục đích là để có các plugin cài đặt bot trong thời gian chạy thông qua một thông báo slack. Vì vậy, nó sẽ cần phải được cập nhật tự động và mỗi plugin này sẽ là độc quyền. –

+0

Bạn có thể mô tả chi tiết hơn về cách hoạt động của nó? 1. Bot nhận lệnh để cài đặt một số plugin? "Cài đặt" là gì? Nếu bot tải xuống hoặc sao chép tệp này từ một số địa điểm thì sao? Hoặc nó chỉ cần gọi một số chức năng từ tập tin đã có? 2. Bot gọi một hàm hoặc nó phải khởi chạy một tập lệnh? Làm sao? –

Trả lời

1

Đã hơn 5 năm, kể từ lần đầu tiên tôi tự hỏi mình gần như cùng một câu hỏi và câu hỏi của bạn bây giờ là một xung để cuối cùng tìm ra.

Đối với tôi nó cũng thú vị, nếu người ta có thể thêm điểm vào từ cùng thư mục với tập lệnh mà không cần cài đặt gói. Mặc dù tôi luôn biết rằng nội dung duy nhất của gói có thể là một số meta với các điểm vào xem xét một số gói khác.

Dù sao, đây là một số thiết lập của thư mục của tôi:

ep_test newtover$ tree 
. 
├── foo-0.1.0.dist-info 
│   ├── METADATA 
│   └── entry_points.txt 
└── foo.py 

1 directory, 3 files 

Dưới đây là nội dung của foo.py:

ep_test newtover$ cat foo.py 
def foo1(): 
    print 'foo1' 

def foo2(): 
    print 'foo2' 

Bây giờ chúng ta hãy mở ipython:

In [1]: def write_ep(lines): # a helper to update entry points file 
    ...:  with open('foo-0.1.0.dist-info/entry_points.txt', 'w') as f1: 
    ...:   print >> f1, '\n'.join(lines) 
    ...:   

In [2]: write_ep([ # only one entry point under foo.test 
    ...: "[foo.test]", 
    ...: "foo_1 = foo:foo1", 
    ...: ]) 

In [3]: !cat foo-0.1.0.dist-info/entry_points.txt 
[foo.test] 
foo1 = foo:foo1 

In [4]: import pkg_resources 

In [5]: ws = pkg_resources.WorkingSet() # here is the answer on the question 

In [6]: list(ws.iter_entry_points('foo.test')) 
Out[6]: [EntryPoint.parse('foo_1 = foo:foo1')] 

In [7]: write_ep([ # two entry points 
    ...: "[foo.test]", 
    ...: "foo_1 = foo:foo1", 
    ...: "foo_2 = foo:foo2" 
    ...: ]) 

In [8]: ws = pkg_resources.WorkingSet() # a new instance of WorkingSet 

Với các thông số mặc định WorkingSet chỉ cần truy cập lại từng mục nhập trong sys.p nhưng bạn có thể thu hẹp danh sách. pkg_resources.iter_entry_points được liên kết với một phiên bản toàn cầu là WorkingSet.

In [9]: list(ws.iter_entry_points('foo.test')) # both are visible 
Out[9]: [EntryPoint.parse('foo_1 = foo:foo1'), EntryPoint.parse('foo_2 = foo:foo2')] 

In [10]: foos = [ep.load() for ep in ws.iter_entry_points('foo.test')] 

In [11]: for func in foos: print 'name is {}'.format(func.__name__); func() 
name is foo1 
foo1 
name is foo2 
foo2 

Và nội dung của METADATA cũng như:

ep_test newtover$ cat foo-0.1.0.dist-info/METADATA 
Metadata-Version: 1.2 
Name: foo 
Version: 0.1.0 
Summary: entry point test 

UPD1: Tôi nghĩ nó trên một lần nữa và bây giờ hiểu rằng bạn cần một bước bổ sung trước khi sử dụng các plugin mới: bạn cần phải tải lại các mô-đun.

Điều này có thể dễ dàng như:

In [33]: modules_to_reload = {ep1.module_name for ep1 in ws.iter_entry_points('foo.test')} 

In [34]: for module_name in modules_to_reload: 
    ....:  reload(__import__(module_name)) 
    ....: 

Nhưng nếu một phiên bản mới của gói plugin của bạn dựa trên những thay đổi đáng kể trong các module đã qua sử dụng khác, bạn có thể cần một thứ tự cụ thể của tải lại và nạp lại những thay đổi mô-đun. Điều này có thể trở thành một nhiệm vụ rườm rà, do đó khởi động lại bot sẽ là cách duy nhất để đi.

0

Tôi cần làm điều gì đó tương tự để tải plugin giả cho mục đích thử nghiệm. Điều này hơi khác so với trường hợp sử dụng của bạn ở chỗ tôi đã cố gắng tránh việc xác định các điểm vào trong gói (vì nó chỉ là mã kiểm thử).

tôi thấy tôi tự động có thể chèn ghi vào pkg_resources cấu trúc dữ liệu như sau:

import pkg_resources 
# Create the fake entry point definition 
ep = pkg_resources.EntryPoint.parse('dummy = dummy_module:DummyPlugin') 

# Create a fake distribution to insert into the global working_set 
d = pkg_resources.Distribution() 

# Add the mapping to the fake EntryPoint 
d._ep_map = {'namespace': {'dummy': ep}} 

# Add the fake distribution to the global working_set 
pkg_resources.working_set.add(d, 'dummy') 

này, tại thời gian chạy, thêm một điểm vào gọi là 'giả' thành 'namespace', đó sẽ là lớp 'DummyPlugin' trong 'dummy_module.py'.

Điều này được xác định thông qua việc sử dụng tài liệu setuptools và dir() trên các đối tượng để có thêm thông tin khi cần.

Documents đang ở đây: http://setuptools.readthedocs.io/en/latest/pkg_resources.html

Bạn đặc biệt là có thể nhìn vào http://setuptools.readthedocs.io/en/latest/pkg_resources.html#locating-plugins nếu tất cả các bạn cần làm là tải một plugin mà bạn vừa lưu trữ để hệ thống tập tin địa phương của bạn.

Các vấn đề liên quan