2013-02-12 8 views
5

Buduję bardzo podstawową platformę w postaci modułu Python 2.7. Moduł ten ma pętlę odczytu-eval-print, w której wprowadzone polecenia użytkownika są odwzorowywane na wywołania funkcji. Ponieważ staram się ułatwić budowanie modułów wtyczek dla mojej platformy, wywołania funkcji będą pochodzić z mojego głównego modułu do dowolnego modułu wtyczki. Chciałbym, żeby konstruktor wtyczek mógł określić polecenie, które chce wywołać jego funkcję, więc szukałem sposobu Pythonicznego, aby zdalnie wprowadzić mapowanie w poleceniu-> funkcja dict w module głównym z moduł wtyczki.Najbardziej Pythonowy sposób dostarczania metadanych funkcji w czasie kompilacji?

szukałem w kilku rzeczy: imię

  1. metody analizowania: moduł główny będzie importować moduł wtyczki i zeskanować go do nazwy metody pasujących do pewnego formatu. Dla przykładu może dodać metodę download_file_command (file) do jej dyktafonu jako "download file" -> download_file_command. Jednak uzyskanie zwięzłej, łatwej do napisania nazwy polecenia (np. "Dl") wymaga, aby nazwa funkcji była krótka, co nie jest dobre dla czytelności kodu . Wymaga to także, aby programista wtyczki dostosował się do precyzyjnego formatu nazw na podstawie .

  2. Cross-moduł dekoratorzy: dekoratorzy pozwoliłby dewelopera wtyczki nazwać swoją funkcję, co chce i po prostu dodać coś takiego @ Main.register („dl”), ale oni muszą wymagać, że zarówno zmodyfikować przestrzeń nazw innego modułu i zachować globalny stan w module głównym. Rozumiem, że to bardzo źle.

  3. samo-moduł dekoratorzy: wykorzystujące tę samą logikę jak wyżej, mogę dodać dekorator, który dodaje nazwą funkcji do jakiegoś polecenia nazwa-> mapowanie funkcji lokalnego modułu wtyczki i odzyskać mapowanie do Główny moduł z wywołaniem API . Wymaga to, aby pewne metody zawsze były obecne lub zostały odziedziczone, i - jeśli moje rozumienie dekoratorów jest poprawne - funkcja zarejestruje się tylko przy pierwszym uruchomieniu i będzie niepotrzebnie rejestrować się ponownie po każdym następnym czasie.

Tak więc, co naprawdę potrzebne jest pythonowy sposobem opisywania funkcję o nazwie polecenia, które powinny skutkować iw ten sposób nie może być nazwą funkcji. Muszę być w stanie wyodrębnić nazwę polecenia-> odwzorowanie funkcji, gdy importuję moduł, a mniej pracy po stronie dewelopera wtyczki jest dużym plusem.

Dziękuję za pomoc i przepraszam, jeśli w moim zrozumieniu Pythona są jakieś błędy; Jestem stosunkowo nowy w tym języku.

+1

Tylko sprawdzam sobie sprawę, ale dekoratorzy (definicje i funkcyjne) wykonać w czasie wykonywania, jedyną rzeczą, Python robi w czasie kompilacji jest kompilacji kodu. – Duncan

+1

Nic złego w metodzie 2. Niech zrobią coś takiego jak "importuj wtyczkę" i wykonaj '@ plugin.register ('moja_nazwa_metody') \ ndef somefunc_meeting_an_api_spec(): ...' –

+0

Możesz chcieć rzucić okiem na to jak [plac] (http://pypi.python.org/pypi/plac) to robi. – georg

Odpowiedz

4

Funkcje zdefiniowane przez użytkownika mogą mieć dowolne atrybuty. Można więc określić, że funkcje wtyczek mają atrybut o określonej nazwie. Na przykład:

następnie w module można zbudować mapowanie takiego:

import inspect #from standard library 

import plugin 

mapping = {} 

for v in plugin.__dict__.itervalues(): 
    if inspect.isfunction(v) and v.hasattr('command_name'): 
     mapping[v.command_name] = v 

czytać o arbitralnych atrybutów zdefiniowanych przez użytkownika funkcji znajduje the docs

+0

To jest dokładnie ten typ rzeczy, którego szukałem! – mieubrisse

+0

Rozumiem, że to pytanie jest trochę stare, ale zobacz moje powiązane pytanie tutaj http://stackoverflow.com/questions/37192712/how-can-attributes-on-functions-survive-wrapping. Czy napotkasz problemy z pakowaniem przy użyciu atrybutów przechowywanych w funkcjach? – matthewatabet

1

Istnieją dwie części w systemie wtyczek:

  1. Odkrywanie wtyczek
  2. wyzwalania niektóre wykonanie kodu w plugin

Proponowane rozwiązania w adresie zapytania tylko drugiej części.

Istnieje wiele sposobów, aby wdrożyć zarówno w zależności od potrzeb np włączyć wtyczek, mogą one być określone w pliku konfiguracyjnym dla aplikacji:

plugins = some_package.plugin_for_your_app 
    another_plugin_module 
    # ... 

wdrożyć ładowanie modułów wtyczkę:

plugins = [importlib.import_module(name) for name in config.get("plugins")] 

Aby uzyskać słownika: command name -> function:

commands = {name: func 
      for plugin in plugins 
      for name, func in plugin.get_commands().items()} 

Autor wtyczek może użyć dowolnej metody do implementacji get_commands() np. Używając prefiksów lub dekoratorów - Twoja główna aplikacja nie powinna się przejmować, dopóki get_commands() zwraca słownik poleceń dla każdej wtyczki.

Przykładowo some_plugin.py (pełne źródło)

def f(a, b): 
    return a + b 

def get_commands(): 
    return {"add": f, "multiply": lambda x,y: x*y} 

Definiuje dwa polecenia add, multiply.

+0

Dziękuję; Podoba mi się wyjaśnienie projektu. To wyjaśnienie sugeruje, że superklasa, która implementuje listę get_commands(), może być dobrym pomysłem, dlatego programiści muszą wykonać jak najmniej pracy. – mieubrisse

+0

@miebrisse: żadne zajęcia nie są wymagane. Dodałem pełny przykład wtyczki. – jfs

4

budynku lub stojącej na Pierwsza część odpowiedzi @ ericstalbot, może Ci się przydać użycie dekoratora takiego jak poniżej.

################################################################################ 
import functools 
def register(command_name): 
    def wrapped(fn): 
     @functools.wraps(fn) 
     def wrapped_f(*args, **kwargs): 
      return fn(*args, **kwargs) 
     wrapped_f.__doc__ += "(command=%s)" % command_name 
     wrapped_f.command_name = command_name 
     return wrapped_f 
    return wrapped 
################################################################################ 
@register('cp') 
def copy_all_the_files(*args, **kwargs): 
    """Copy many files.""" 
    print "copy_all_the_files:", args, kwargs 
################################################################################ 

print "Command Name: ", copy_all_the_files.command_name 
print "Docstring : ", copy_all_the_files.__doc__ 

copy_all_the_files("a", "b", keep=True) 

Wyjście podczas uruchamiania:

Command Name: cp 
Docstring : Copy many files.(command=cp) 
copy_all_the_files: ('a', 'b') {'keep': True} 
Powiązane problemy