Zawsze używałem plików JSON do konfiguracji moich aplikacji. Zacząłem ich używać od czasu, gdy napisałem dużo kodu Java, a teraz pracuję głównie nad rozwojem Pythona po stronie serwera i nauki danych i nie jestem pewien, czy JSON jest już właściwą drogą.
Widziałem, że Celery używa rzeczywistych plików Python do konfiguracji. Początkowo byłem sceptyczny. Ale pomysł użycia prostych struktur danych w Pythonie do konfiguracji zaczyna mi się podobać. Niektóre zalety:
- Struktury danych będą takie same, jak normalnie koduję. Nie muszę więc zmieniać zdania.
- Moje IDE (PyCharm) rozumie związek między konfiguracją a kodem. Ctrl+ Bumożliwia łatwe przechodzenie między konfiguracją a kodem.
- Nie muszę pracować z IMO niepotrzebnym ścisłym JSON . Patrzę na ciebie podwójne cudzysłowy, bez przecinków i komentarzy.
- Mogę pisać konfiguracje testowe w aplikacji, nad którą pracuję, a następnie łatwo przenosić je do pliku konfiguracyjnego bez konieczności jakiejkolwiek konwersji i analizy JSON.
- W razie potrzeby można wykonać bardzo proste skrypty w pliku konfiguracyjnym. (Chociaż powinno to być bardzo, bardzo ograniczone.)
Więc moje pytanie brzmi: jeśli się przełączę, jak strzelę sobie w stopę?
Żaden niewykwalifikowany użytkownik końcowy nie będzie korzystał z plików konfiguracyjnych. Wszelkie zmiany w plikach konfiguracyjnych są obecnie zatwierdzane przez Git i są wdrażane na nasze serwery w ramach ciągłego wdrażania. Nie ma żadnych ręcznych zmian konfiguracji, chyba że zdarzy się sytuacja wyjątkowa lub jest w fazie rozwoju.
(Rozważałem YAML , ale coś w tym mnie denerwuje. Więc na razie nie ma go na amerykańskim stole).
źródło
Odpowiedzi:
Używając języka skryptowego zamiast pliku konfiguracyjnym świetnie wygląda na pierwszy rzut oka: masz pełną moc tego języka dostępne i można po prostu
eval()
alboimport
nie. W praktyce istnieje kilka gotchas:jest to język programowania, którego należy się nauczyć. Aby edytować konfigurację, musisz wystarczająco dobrze znać ten język. Pliki konfiguracyjne mają zwykle prostszy format, który trudniej jest pomylić.
jest to język programowania, co oznacza, że konfiguracja może być trudna do debugowania. Za pomocą normalnego pliku konfiguracyjnego można na niego spojrzeć i zobaczyć, jakie wartości podano dla każdej właściwości. Za pomocą skryptu możesz go najpierw wykonać, aby zobaczyć wartości.
jest to język programowania, który utrudnia zachowanie wyraźnego rozdziału między konfiguracją a rzeczywistym programem. Czasami chcesz tego rodzaju rozszerzalności, ale w tym momencie prawdopodobnie szukasz prawdziwego systemu wtyczek.
jest to język programowania, co oznacza, że config może zrobić wszystko, co potrafi język programowania. Więc albo używasz rozwiązania piaskownicy, które neguje dużą elastyczność języka, albo pokładasz duże zaufanie w autorze konfiguracji.
Zatem użycie skryptu do konfiguracji jest prawdopodobnie OK, jeśli odbiorcami twojego narzędzia są programiści, np. Sphinx config lub setup.py w projektach Python. Inne programy z konfiguracją wykonywalną to powłoki takie jak Bash i edytory takie jak Vim.
Użycie języka programowania do konfiguracji jest konieczne, jeśli config zawiera wiele sekcji warunkowych lub jeśli zapewnia wywołania zwrotne / wtyczki. Używanie skryptu bezpośrednio zamiast eval () - niektóre pola konfiguracji wydają się być bardziej debugowalne (pomyśl o śladach stosu i numerach linii!).
Dobrym pomysłem może być również bezpośrednie użycie języka programowania, jeśli konfiguracja jest tak powtarzalna, że piszesz skrypty do automatycznego generowania konfiguracji. Ale może lepszy model danych dla konfiguracji mógłby wyeliminować potrzebę takiej jawnej konfiguracji? Na przykład może być pomocne, jeśli plik konfiguracyjny może zawierać symbole zastępcze, które później rozwiniesz. Inną często widzianą funkcją jest wiele plików konfiguracyjnych o różnym priorytecie, które mogą się nadpisywać, choć wprowadza to pewne problemy.
W większości przypadków pliki INI, pliki właściwości Java lub dokumenty YAML są znacznie lepiej dostosowane do konfiguracji. W przypadku złożonych modeli danych może również obowiązywać XML. Jak zauważyłeś, JSON ma pewne aspekty, które sprawiają, że nie nadaje się jako plik konfiguracyjny do edycji przez człowieka, chociaż jest to dobry format wymiany danych.
źródło
sendmail.cf
. Oznaczałoby to, że użycie rzeczywistego języka skryptowego może być korzystne, ponieważ w rzeczywistości został on zaprojektowany tak, aby był kompletny w Turingu. Jednakże Turing-kompletność i „Tetris kompletność” to dwie różne rzeczy, a jednocześniesendmail.cf
może obliczyć dowolne funkcje, może nie wysyłać swojej/etc/passwd
nad siatką lub formatowania dysku twardym, który Python lub Perl byłby w stanie.+1 do wszystkiego w odpowiedzi amona . Chciałbym dodać to:
Pożałujesz użycia kodu Python jako języka konfiguracji przy pierwszym zaimportowaniu tej samej konfiguracji z kodu napisanego w innym języku. Na przykład, jeśli kod, który jest częścią twojego projektu i jest napisany w C ++ lub Ruby lub coś innego wymaga pobrania konfiguracji, musisz połączyć interpreter Pythona jako bibliotekę lub przeanalizować konfigurację w koprocesie Pythona, oba które są niezręczne, trudne lub nadmiernie kosztowne.
Cały kod, który dzisiaj importuje tę konfigurację, może być napisany w Pythonie, i możesz pomyśleć, że będzie to również jutro, ale czy wiesz na pewno?
Powiedziałeś, że użyjesz logiki (cokolwiek innego niż statyczne struktury danych) w swojej konfiguracji oszczędnie, jeśli w ogóle, co jest dobre, ale jeśli w ogóle jest jej trochę, w przyszłości będzie trudno ją cofnąć, więc może wrócić do deklaratywnego pliku konfiguracyjnego.
EDYCJA dla rekordu: kilka osób skomentowało tę odpowiedź na temat tego, jak prawdopodobne lub mało prawdopodobne jest, że projekt zostanie kiedykolwiek całkowicie przepisany w innym języku. Można śmiało powiedzieć, że kompletne przepisywanie wsteczne jest prawdopodobnie rzadko spotykane. W rzeczywistości miałem na myśli fragmenty tego samego projektu (wymagającego dostępu do tej samej konfiguracji) napisane w różnych językach. Na przykład serwowanie stosu w C ++ dla szybkości, wsadowe czyszczenie bazy danych w Pythonie, niektóre skrypty powłoki jako klej. Zastanów się też nad tą sprawą :)
źródło
key=value
przypisań do konfiguracji, nie rozumiem, dlaczego program Java / C ++ nie mógł odczytać pliku Python jako zwykłego pliku tekstowego i przeanalizować go tak samo, jeśli trzeba przejść do czegoś innego w przyszłość. Nie widzę potrzeby pełnoprawnego interpretera języka Python.Inne odpowiedzi są już bardzo dobre, przedstawię swoje doświadczenia związane ze stosowaniem w świecie rzeczywistym w kilku projektach.
Plusy
W większości są już określone:
eval
); działa automatycznie nawet w przypadku bardziej złożonych typów danych (w naszym programie mamy punkty geometryczne i transformacje, które są zrzucane / ładowane dokładnie przezrepr
/eval
);Cons
repr
. To oczywiście zła rzecz.Nawet jeśli jesteś w Pythonie, modyfikacja pliku konfiguracyjnego z kodu jest prawdziwym problemem, ponieważ ... no cóż, modyfikacja kodu wcale nie jest trywialna, szczególnie kod, który ma bogatą składnię i nie jest w LISP lub podobny. Jeden z naszych programów ma plik konfiguracyjny, którym jest Python, napisany ręcznie ręcznie, ale który później okazał się przydatny do manipulowania za pomocą oprogramowania (szczególnym ustawieniem jest lista rzeczy, których porządkowanie przy użyciu GUI jest znacznie łatwiejsze). To duży problem, ponieważ:
Porównaj to z JSON, INI lub (Boże nie!) XML, gdzie reprezentację w pamięci można zawsze edytować i zapisywać bez utraty danych (XML, gdzie większość parserów DOM może zachować białe znaki w węzłach tekstowych i węzłach komentarzy) lub przynajmniej utrata tylko formatowania (JSON, gdzie sam format nie pozwala na więcej niż czytane nieprzetworzone dane).
Jak zwykle nie ma jednoznacznego rozwiązania; moja obecna polityka w tej kwestii to:
jeśli plik konfiguracyjny to:
plik Python może być prawidłowym pomysłem;
jeśli zamiast tego:
format „tylko dane” może być lepszym pomysłem.
Zauważ, że nie trzeba dokonywać jednego wyboru - niedawno napisałem aplikację, która wykorzystuje oba podejścia. Mam prawie nigdy nie zmodyfikowany plik z pierwszą konfiguracją, odręcznymi ustawieniami, w których są zalety posiadania fajnych bonusów w Pythonie, oraz plik JSON do konfiguracji edytowany z interfejsu użytkownika.
źródło
note:
pole, które jest ignorowane w konfiguracji.curl ... | bash
, jest to mniej kłopotliwe. :-PGłówne pytanie brzmi: czy chcesz, aby plik konfiguracyjny był w jakimś kompletnym języku Turinga (takim jak Python)? Jeśli tego chcesz, możesz również rozważyć osadzenie innego języka skryptowego (kompletnego Turinga), takiego jak Guile lub Lua (ponieważ może być postrzegany jako „prostszy” w użyciu lub osadzaniu niż Python; przeczytaj rozdział Rozszerzanie i Osadzanie Pythona ). Nie będę o tym dalej dyskutować (ponieważ inne odpowiedzi - na przykład Amon - omawiał to dogłębnie), ale zauważam, że osadzenie języka skryptowego w twojej aplikacji jest ważnym wyborem architektonicznym , który powinieneś rozważyć bardzo wcześnie; Naprawdę nie polecam dokonywania tego wyboru później!
Dobrze znanym przykładem programu konfigurowalnego przez „skrypty” jest edytor GNU emacs (lub prawdopodobnie AutoCAD w sferze zastrzeżonej); więc miej świadomość, że jeśli zaakceptujesz skrypty, jakiś użytkownik w końcu skorzysta - i być może nadużywanie, z twojego punktu widzenia - z tego narzędzia i stworzy skrypt wielotysięczny; dlatego wybór wystarczająco dobrego języka skryptowego jest ważny.
Jednak (przynajmniej w systemach POSIX), możesz uznać za wygodne włączenie dynamicznego obliczania „pliku” konfiguracji w czasie inicjalizacji (oczywiście pozostawiając ciężar rozsądnej konfiguracji administratorowi systemu lub użytkownikowi; w rzeczywistości jest to konfiguracja tekst pochodzący z jakiegoś pliku lub polecenia). W tym celu możesz po prostu przyjąć konwencję (i udokumentować ją), że ścieżka pliku konfiguracyjnego zaczynająca się od np. A
!
lub a|
jest w rzeczywistości poleceniem powłoki , które czytałbyś jako potok . To pozostawia użytkownikowi możliwość wyboru dowolnego „preprocesora” lub „języka skryptowego”, który jest mu najbardziej znany.(jeśli użytkownik akceptuje konfigurację obliczaną dynamicznie, musi zaufać użytkownikowi w kwestiach bezpieczeństwa)
Zatem w kodzie inicjalizacyjnym
main
(na przykład) zaakceptujesz jakiś--config
argumentconfarg
i wyciągnieszFILE*configf;
z niego trochę . Jeśli ten argument zaczyna się od!
(tzn. Jeśli(confarg[0]=='!')
....), użyjeszconfigf = popen(confarg+1, "r");
i zamkniesz ten potok za pomocąpclose(configf);
. W przeciwnym razie użyjeszconfigf=fopen(confarg, "r");
i zamkniesz ten plik za pomocąfclose(configf);
(nie zapomnij o sprawdzaniu błędów). Patrz rura (7) , popen (3) , fopen (3) . Aby zapoznać się z aplikacją zakodowaną w języku Python, przeczytaj o os.popen itp.(dokument również dla dziwnego użytkownika, który chce przekazać plik konfiguracyjny o nazwie
!foo.config
pass,./!foo.config
aby ominąćpopen
powyższą sztuczkę)Przy okazji, taka sztuczka jest jedynie wygodą (aby uniknąć wymagania od zaawansowanego użytkownika np. Kodowania jakiegoś skryptu powłoki w celu wygenerowania pliku konfiguracyjnego ). Jeśli użytkownik chce zgłosić błąd, powinien wysłać wygenerowany plik konfiguracyjny ...
Zauważ, że możesz również zaprojektować swoją aplikację z możliwością używania i ładowania wtyczek w czasie inicjalizacji, np. Za pomocą dlopen (3) (i musisz zaufać swojemu użytkownikowi w kwestii tej wtyczki). Ponownie, jest to bardzo ważna decyzja architektoniczna (i musisz zdefiniować i dostarczyć trochę raczej stabilnego API i konwencji dotyczących tych wtyczek i twojej aplikacji).
W przypadku aplikacji napisanej w języku skryptowym, takim jak Python, można również zaakceptować argument programu dla eval lub exec lub podobnych prymitywów. Ponownie, problemy związane z bezpieczeństwem są wówczas przedmiotem zainteresowania (zaawansowanego) użytkownika.
Jeśli chodzi o format tekstowy pliku konfiguracyjnego ( niezależnie od tego, czy jest generowany, czy nie), uważam, że najczęściej musisz go dobrze udokumentować (a wybór określonego formatu nie jest tak ważny; zalecam jednak, aby użytkownik mógł niektóre -przesłane-komentarze w nim). Możesz użyć JSON (najlepiej z niektórym parserem JSON, który akceptuje i pomija komentarze ze zwykłymi
//
do eol lub/*
...*/
...), YAML, XML, INI lub własną rzeczą. Analiza pliku konfiguracyjnego jest dość łatwa (a znajdziesz wiele bibliotek związanych z tym zadaniem).źródło
Czy dodając do odpowiedzi amona , zastanawiałeś się nad alternatywami? JSON to może więcej niż potrzebujesz, ale pliki Pythona prawdopodobnie spowodują problemy w przyszłości z powodów wymienionych powyżej.
Jednak Python ma już parser konfiguracji dla bardzo prostego języka konfiguracji, który może spełnić wszystkie Twoje potrzeby.
ConfigParser
Moduł implementuje prosty język config.źródło
Od dłuższego czasu pracuję z dobrze znanym oprogramowaniem, którego pliki konfiguracyjne są zapisane w języku TCL, więc pomysł nie jest nowy. Działa to całkiem dobrze, ponieważ użytkownicy, którzy nie znają języka, mogą nadal pisać / edytować proste pliki konfiguracyjne za pomocą pojedynczej
set name value
instrukcji, podczas gdy bardziej zaawansowani użytkownicy i programiści mogą wyciągać z tego wyrafinowane sztuczki.Nie sądzę, że „pliki konfiguracyjne mogą być trudne do debugowania” jest ważnym problemem. Tak długo, jak aplikacja nie zmusza użytkowników do pisania skryptów, użytkownicy mogą zawsze używać prostych przypisań w swoich plikach konfiguracyjnych, co nie jest trudniejsze do uzyskania w porównaniu do JSON lub XML.
Przepisywanie konfiguracji jest problemem, choć nie jest tak złe, jak się wydaje. Aktualizacja dowolnego kodu jest niemożliwa, ale ładowanie konfiguracji z pliku, modyfikowanie go i zapisywanie z powrotem jest. Zasadniczo, jeśli wykonasz skrypty w pliku konfiguracyjnym, który nie jest tylko do odczytu, po zapisaniu skończy się odpowiednia lista
set name value
instrukcji. Dobrą wskazówką, że tak się stanie, jest komentarz „nie edytuj” na początku pliku.Jedną rzeczą do rozważenia jest to, że twoje pliki konfiguracyjne nie będą niezawodnie odczytywane przez proste narzędzia oparte na wyrażeniach regularnych, takie jak
sed
, ale o ile rozumiem, nie jest tak już w przypadku twoich obecnych plików JSON, więc nie ma wiele do stracenia.Tylko upewnij się, że używasz odpowiednich technik piaskownicy podczas wykonywania plików konfiguracyjnych.
źródło
Oprócz wszystkich ważnych punktów innych dobrych odpowiedzi tutaj (wow, nawet wspomnieli o koncepcji Turinga-kompletnego), istnieje kilka solidnych praktycznych powodów, aby NIE używać pliku Python jako konfiguracji, nawet jeśli pracujesz na Pythonie- tylko projekt.
Ustawienia w pliku źródłowym Pythona są technicznie częścią wykonywalnego kodu źródłowego, a nie pliku danych tylko do odczytu. Jeśli pójdziesz tą drogą, zazwyczaj tak zrobisz
import config
, ponieważ tego rodzaju „wygoda” była prawdopodobnie jednym z głównych powodów, dla których ludzie zaczęli od używania pliku Python jako konfiguracji. Teraz masz tendencję do zatwierdzania pliku config.py w repozytorium, w przeciwnym razie użytkownik końcowy napotka mylący błąd ImportError, gdy spróbuje uruchomić program po raz pierwszy.Zakładając, że faktycznie zatwierdzasz plik config.py w repozytorium, teraz członkowie Twojego zespołu prawdopodobnie mieliby inne ustawienia w innym środowisku. Wyobraź sobie, że pewnego dnia jakiś członek przypadkowo zatwierdza swój lokalny plik konfiguracyjny do repozytorium.
Wreszcie, twój projekt może mieć hasła w pliku konfiguracyjnym. (To samo w sobie jest dyskusyjną praktyką, ale i tak się dzieje.) A jeśli plik konfiguracyjny istnieje w repozytorium, ryzykujesz, że poświadczysz publiczne repozytorium.
Teraz, używając pliku konfiguracyjnego zawierającego tylko dane, takiego jak uniwersalny format JSON, można uniknąć wszystkich 3 powyższych problemów, ponieważ możesz rozsądnie poprosić użytkownika o wymyślenie własnego pliku config.json i przesłanie go do programu.
PS: To prawda, że JSON ma wiele ograniczeń. 2 z ograniczeń wymienionych przez PO można rozwiązać za pomocą kreatywności.
I zwykle mam symbol zastępczy, aby ominąć regułę przecinka końcowego. Lubię to:
źródło