Làm thế nào xấu của một ý tưởng là sử dụng các tệp Python làm các tệp cấu hình?


72

Tôi đã luôn sử dụng các tệp JSON để cấu hình các ứng dụng của mình. Tôi đã bắt đầu sử dụng chúng từ khi tôi mã hóa rất nhiều Java và bây giờ tôi đang làm việc chủ yếu về khoa học dữ liệu và phía máy chủ và không chắc chắn liệu JSON có phải là cách phù hợp nữa hay không.

Tôi đã thấy Celery sử dụng các tệp Python thực tế để cấu hình. Ban đầu tôi đã hoài nghi về nó. Nhưng ý tưởng sử dụng các cấu trúc dữ liệu Python đơn giản để cấu hình đang bắt đầu phát triển trong tôi. Một số ưu điểm:

  • Các cấu trúc dữ liệu sẽ giống như tôi thường mã hóa. Vì vậy, tôi không cần thay đổi khung suy nghĩ.
  • IDE của tôi (PyCharm) hiểu được kết nối giữa cấu hình và mã. Ctrl+ Blàm cho nó có thể nhảy giữa cấu hình và mã dễ dàng.
  • Tôi không cần phải làm việc với JSON nghiêm ngặt không cần thiết của IMO . Tôi đang nhìn vào bạn hai dấu ngoặc kép, không có dấu phẩy và không có bình luận.
  • Tôi có thể viết các cấu hình thử nghiệm trong ứng dụng tôi đang làm việc, sau đó dễ dàng chuyển chúng sang tệp cấu hình mà không phải thực hiện bất kỳ chuyển đổi và phân tích cú pháp JSON nào.
  • Có thể thực hiện kịch bản rất đơn giản trong tệp cấu hình nếu thực sự cần thiết. (Mặc dù điều này nên rất, rất hạn chế.)

Vì vậy, câu hỏi của tôi là: Nếu tôi chuyển đổi, làm thế nào tôi tự bắn vào chân mình?

Không có người dùng cuối không có kỹ năng sẽ được sử dụng các tập tin cấu hình. Mọi thay đổi đối với các tệp cấu hình hiện được cam kết với Git và được triển khai cho các máy chủ của chúng tôi như là một phần của việc triển khai liên tục. Không có thay đổi cấu hình thủ công, trừ khi có trường hợp khẩn cấp hoặc nó đang được phát triển.

(Tôi đã xem xét YAML , nhưng một cái gì đó về nó làm tôi khó chịu. Vì vậy, bây giờ nó đã ra khỏi bàn của Mỹ.)


39
Không có kỹ năng không phải là vấn đề của bạn. Độc hại là.
Blrfl

9
Bạn có ý nghĩa gì khi "rời khỏi bàn Mỹ" ?
Peter Mortensen

24
"Vì vậy, bây giờ nó ra khỏi bàn của Mỹ." === "Vì vậy, bây giờ, như người Mỹ nói, ra khỏi bàn."
giám mục

7
Nếu bạn không thích JSON, bạn nên thử yaml. Tôi thích nó cho cấu hình rất nhiều. đặc biệt là khi các chuỗi lớn hơn có liên quan, YAML sẽ dễ đọc hơn JSON.
Christian Sauer

5
@bishop "khỏi bàn" trong tiếng Anh Anh có nghĩa là không còn được xem xét, từ các chuyển động của quốc hội được đặt trên bàn ở giữa Hạ viện để tham khảo khi được thảo luận, do đó cũng được 'thảo luận để thảo luận' (Hồ sơ Nghị viện 1799 - Books.google.co.uk/ mẹo ), AFAIK nghĩa của Hoa Kỳ là như nhau, nhưng tôi không biết nếu bạn có một bảng trong quốc hội của bạn.
Pete Kirkham

Câu trả lời:


92

Sử dụng ngôn ngữ kịch bản thay cho tệp cấu hình thoạt nhìn trông rất tuyệt: bạn có toàn bộ sức mạnh của ngôn ngữ đó và có thể đơn giản eval()hoặc có thể import. Trong thực tế, có một vài vấn đề

  • nó là một ngôn ngữ lập trình, cần phải học. Để chỉnh sửa cấu hình, bạn cần biết đủ ngôn ngữ này. Các tệp cấu hình thường có định dạng đơn giản, khó bị sai hơn.

  • nó là ngôn ngữ lập trình, có nghĩa là cấu hình có thể khó gỡ lỗi. Với một tệp cấu hình bình thường, bạn nhìn vào nó và xem những giá trị nào được cung cấp cho mỗi thuộc tính. Với một tập lệnh, bạn có khả năng cần phải thực thi nó trước để xem các giá trị.

  • nó là ngôn ngữ lập trình, gây khó khăn cho việc duy trì sự tách biệt rõ ràng giữa cấu hình và chương trình thực tế. Đôi khi bạn thực sự muốn loại mở rộng này, nhưng tại thời điểm đó có lẽ bạn đang tìm kiếm một hệ thống plugin thực sự.

  • nó là ngôn ngữ lập trình, có nghĩa là cấu hình có thể làm bất cứ điều gì mà ngôn ngữ lập trình có thể làm. Vì vậy, hoặc bạn đang sử dụng giải pháp hộp cát phủ nhận phần lớn tính linh hoạt của ngôn ngữ hoặc bạn đang đặt niềm tin cao vào tác giả cấu hình.

Vì vậy, sử dụng tập lệnh cho cấu hình có thể ổn nếu đối tượng của công cụ của bạn là nhà phát triển, ví dụ: Sphinx config hoặc setup.py trong các dự án Python. Các chương trình khác có cấu hình thực thi là các shell như Bash và các trình soạn thảo như Vim.

Sử dụng ngôn ngữ lập trình để cấu hình là cần thiết nếu cấu hình chứa nhiều phần có điều kiện hoặc nếu nó cung cấp hàm gọi lại / bổ trợ. Sử dụng tập lệnh trực tiếp thay vì eval () - trong trường cấu hình nào đó có xu hướng dễ gỡ lỗi hơn (hãy nghĩ đến dấu vết ngăn xếp và số dòng!).

Trực tiếp sử dụng ngôn ngữ lập trình cũng có thể là một ý tưởng tốt nếu cấu hình của bạn lặp đi lặp lại đến mức bạn đang viết các tập lệnh để tự động tạo cấu hình. Nhưng có lẽ một mô hình dữ liệu tốt hơn cho cấu hình có thể loại bỏ sự cần thiết cho cấu hình rõ ràng như vậy? Ví dụ: có thể hữu ích nếu tệp cấu hình có thể chứa các trình giữ chỗ mà sau này bạn mở rộng. Một tính năng khác đôi khi được nhìn thấy là nhiều tệp cấu hình với các ưu tiên khác nhau có thể ghi đè lên nhau, mặc dù điều đó gây ra một số vấn đề của riêng nó.

Trong phần lớn các trường hợp, các tệp INI, tệp thuộc tính Java hoặc tài liệu YAML phù hợp hơn cho cấu hình. Đối với các mô hình dữ liệu phức tạp, XML cũng có thể được áp dụng. Như bạn đã lưu ý, JSON có một số khía cạnh khiến nó không phù hợp dưới dạng tệp cấu hình có thể chỉnh sửa được của con người, mặc dù đây là định dạng trao đổi dữ liệu tốt.


25
Có một vài định dạng tệp cấu hình "vô tình hoàn thành Turing", nổi tiếng nhất sendmail.cf. Điều đó sẽ chỉ ra rằng việc sử dụng một ngôn ngữ kịch bản thực tế có thể có lợi, vì ngôn ngữ đó thực sự được thiết kế để hoàn thành Turing. Tuy nhiên , Turing-đầy đủ và "Tetris-đầy đủ" là hai thứ khác nhau và trong khi sendmail.cfcó thể tính toán các hàm tùy ý, nó không thể gửi /etc/passwdqua mạng hoặc định dạng đĩa cứng của bạn, mà Python hoặc Perl có thể làm được.
Jörg W Mittag

3
@ JörgWMittag Sự hoàn thiện của Torino không có nghĩa là có thể gửi mọi thứ qua mạng hoặc truy cập vào đĩa cứng. Đó là, sự hoàn thiện của Torino là về việc xử lý chứ không phải về I / O. Ví dụ: CSS được coi là hoàn chỉnh ở Torino, nhưng nó sẽ không gây rối với bộ nhớ vĩnh viễn của bạn. Bạn đã nói ở nơi khác rằng "Idris là một ngôn ngữ chức năng hoàn toàn thuần túy, do đó, theo định nghĩa không phải là Turing-perfect", điều đó không tuân theo, và rõ ràng nó đã hoàn thành ở Torino. Tôi đã bị thuyết phục khi bạn sử dụng Testris-Complete có nghĩa là ngôn ngữ đã hoàn thành ở Torino nhưng không thể thực hiện I / O đầy đủ ... có vẻ như đó không phải là ý bạn.
Theraot

6
@Theraot: "Total" có nghĩa là nó luôn trả về. Máy Turing có thể thực hiện một vòng lặp vô hạn, tức là nó có khả năng không quay trở lại. Ergo, Idris không thể làm mọi thứ mà Turing Machine làm, điều đó có nghĩa là nó không hoàn chỉnh. Điều này đúng với tất cả các ngôn ngữ được gõ phụ thuộc. Toàn bộ quan điểm của một ngôn ngữ được gõ phụ thuộc là bạn có thể quyết định các thuộc tính tùy ý về các chương trình, trong khi đó, trong ngôn ngữ hoàn chỉnh Turing, bạn thậm chí không thể quyết định các thuộc tính tầm thường như "chương trình này có dừng không?" Tổng số ngôn ngữ theo định nghĩa không phải là Turing-Complete, bởi vì Turing Machines là một phần.
Jörg W Mittag

10
Các định nghĩa của "Turing hoàn tất" là "có thể thực hiện một máy Turing". Định nghĩa của "Tetris-Complete" là "có thể thực hiện Tetris". Toàn bộ quan điểm của định nghĩa này là Turing-đầy đủ đơn giản là không thú vị lắm trong thế giới thực. Có rất nhiều ngôn ngữ hữu ích không hoàn thành Turing, ví dụ HTML, SQL (trước 1999), nhiều DSL khác nhau, v.v. OTOH, Turing-perfect chỉ ngụ ý rằng bạn có thể tính toán các hàm theo số tự nhiên in ra màn hình, truy cập mạng, tương tác với người dùng, HĐH, môi trường, tất cả đều quan trọng.
Jörg W Mittag

4
Lý do tại sao Edwin Brady sử dụng ví dụ này là vì nhiều người nghĩ rằng các ngôn ngữ không hoàn chỉnh Turing có thể được sử dụng cho lập trình mục đích chung. Bản thân tôi cũng từng nghĩ rằng, vì xét cho cùng, nhiều chương trình thú vị về cơ bản là các vòng lặp vô tận mà chúng ta không muốn dừng lại , ví dụ: máy chủ, hệ điều hành, vòng lặp sự kiện trong GUI, vòng lặp trò chơi. Rất nhiều chương trình xử lý dữ liệu vô hạn, ví dụ các luồng sự kiện. Tôi đã từng nghĩ rằng bạn không thể viết nó bằng một ngôn ngữ tổng thể, nhưng tôi đã học được rằng bạn có thể , và vì vậy tôi thấy đó là một ý tưởng tốt để có một thuật ngữ cho khả năng đó.
Jörg W Mittag

50

+1 cho mọi thứ trong câu trả lời của amon . Tôi muốn thêm điều này:

Bạn sẽ hối tiếc khi sử dụng mã Python làm ngôn ngữ cấu hình của mình vào lần đầu tiên bạn muốn nhập cùng một cấu hình từ bên trong mã được viết bằng một ngôn ngữ khác. Ví dụ: nếu mã đó là một phần của dự án của bạn và nó được viết bằng C ++ hoặc Ruby hoặc thứ gì đó khác cần phải cấu hình, bạn sẽ cần liên kết trong trình thông dịch Python dưới dạng thư viện hoặc phân tích cấu hình trong bộ đồng xử lý Python, cả hai đó là vụng về, khó khăn, hoặc chi phí cao.

Tất cả các mã nhập cấu hình này ngày hôm nay có thể được viết bằng Python và bạn có thể nghĩ rằng điều này cũng sẽ đúng vào ngày mai, nhưng bạn có chắc chắn không?

Bạn nói rằng bạn sẽ sử dụng logic (bất cứ thứ gì khác có cấu trúc dữ liệu tĩnh) trong cấu hình của bạn một cách tiết kiệm nếu có, điều đó tốt, nhưng nếu có chút nào đó, trong tương lai bạn sẽ gặp khó khăn trong việc hoàn tác nó có thể di chuyển trở lại một tập tin cấu hình khai báo.

EDIT cho hồ sơ: một số người đã bình luận về câu trả lời này về khả năng một dự án sẽ được viết lại hoàn toàn bằng ngôn ngữ khác. Thật công bằng khi nói rằng một bản viết lại tương thích ngược hoàn toàn có lẽ hiếm khi được nhìn thấy. Những gì tôi thực sự có trong đầu là các bit và phần của cùng một dự án (và cần truy cập vào cùng một cấu hình) được viết bằng các ngôn ngữ khác nhau. Ví dụ: phục vụ ngăn xếp trong C ++ để tăng tốc độ, dọn dẹp cơ sở dữ liệu hàng loạt trong Python, một số tập lệnh shell làm keo dán. Vì vậy, dành một suy nghĩ cho trường hợp đó quá :)


1
@Mast, xin lỗi, nhưng tôi không làm theo. Tên của tệp (có hoặc không kết thúc bằng .py) không ở đây và cũng không có. Điểm tôi đang cố gắng thực hiện là nếu nó được viết bằng Python thì bạn cần một trình thông dịch Python để đọc nó.
Celada

12
@Mast Tôi nghĩ bạn đang phân tích sai. Điểm tôi rút ra từ câu trả lời này (cả bản gốc và bản chỉnh sửa) là lựa chọn ghi tệp cấu hình bằng ngôn ngữ lập trình là nó khiến việc viết mã bằng ngôn ngữ khác trở nên khó khăn hơn. Ví dụ: bạn quyết định chuyển ứng dụng của mình sang Anrdoid / iPhone và sẽ sử dụng ngôn ngữ khác. Bạn phải (a) dựa vào trình thông dịch Python trên ứng dụng điện thoại di động (không lý tưởng), (b) viết lại cấu hình ở định dạng độc lập với ngôn ngữ và viết lại mã Python đã sử dụng hoặc (c) duy trì hai định dạng cấu hình sắp tới.
Jon Bentley

4
@JonBentley Tôi cho rằng mối quan tâm sẽ có liên quan nếu có kế hoạch thực hiện các dự án đa ngôn ngữ. Tôi đã không có được ấn tượng đó từ OP. Ngoài ra, sử dụng tệp văn bản để định cấu hình vẫn yêu cầu mã bổ sung (bằng tất cả các ngôn ngữ) để phân tích / chuyển đổi giá trị thực tế. Về mặt kỹ thuật, nếu họ giới hạn phía Python trong các key=valuebài tập cho cấu hình, tôi không hiểu tại sao chương trình Java / C ++ không thể đọc tệp Python dưới dạng tệp văn bản đơn giản và phân tích tương tự nếu chúng cần chuyển sang một thứ khác trong tương lai. Tôi không thấy cần một trình thông dịch Python chính thức.
code_dredd

3
@ray Đúng, nhưng câu trả lời vẫn hữu ích trên cơ sở những câu hỏi không nên áp dụng cho người đăng chúng. Nếu bạn sử dụng định dạng chuẩn (ví dụ INI, JSON, YAML, XML, v.v.) thì có thể bạn sẽ sử dụng thư viện phân tích cú pháp hiện có thay vì viết riêng. Điều đó làm giảm công việc bổ sung xuống chỉ một lớp bộ điều hợp để giao tiếp với thư viện phân tích cú pháp. Nếu bạn đang tự giới hạn mình với key = value, thì điều đó sẽ loại bỏ hầu hết các lý do của OP để sử dụng Python ở nơi đầu tiên và bạn cũng có thể chỉ cần đi với một định dạng được công nhận.
Jon Bentley

3
Tôi đã phải làm điều này vài năm trước khi một công cụ viết bằng Lua sử dụng tập lệnh Lua làm cấu hình của nó, sau đó họ muốn chúng tôi viết một công cụ mới trong C # và đặc biệt yêu cầu chúng tôi sử dụng tập lệnh cấu hình Lua. Họ có tổng cộng 2 dòng thực sự có thể lập trình được và không đơn giản x = y, nhưng tôi vẫn phải tìm hiểu về các trình thông dịch Lua mã nguồn mở cho .net vì chúng. Đó không phải là một lý luận thuần túy.
Kevin Phí

21

Các câu trả lời khác đã rất tốt, tôi sẽ chỉ mang lại trải nghiệm sử dụng trong thế giới thực trong một vài dự án.

Ưu

Chúng hầu hết đã được đánh vần:

  • nếu bạn đang ở trong một chương trình Python, phân tích cú pháp là một làn gió ( eval); nó hoạt động tự động ngay cả đối với các loại dữ liệu phức tạp hơn (trong chương trình của chúng tôi, chúng tôi có các điểm và biến đổi hình học, được kết xuất / tải tốt chỉ qua repr/ eval);
  • tạo một "cấu hình giả" chỉ với một vài dòng mã là chuyện nhỏ;
  • bạn có cấu trúc tốt hơn và, IMO, cách cú pháp tốt hơn JSON (thậm chí chỉ cần có ý kiến ​​và không phải đặt dấu ngoặc kép quanh các khóa từ điển là một chiến thắng dễ đọc lớn).

Nhược điểm

  • người dùng độc hại có thể làm bất cứ điều gì mà chương trình chính của bạn có thể làm; Tôi không xem xét vấn đề này nhiều vì thông thường nếu người dùng có thể sửa đổi tệp cấu hình thì anh ta / cô ta có thể làm bất cứ điều gì ứng dụng có thể làm;
  • nếu bạn không còn trong chương trình Python, bây giờ bạn có vấn đề. Mặc dù một số tệp cấu hình của chúng tôi vẫn ở chế độ riêng tư đối với ứng dụng gốc của chúng, một tệp đặc biệt đến để lưu trữ thông tin được sử dụng bởi một số chương trình khác nhau, hầu hết trong số đó hiện có trong C ++, hiện có trình phân tích cú pháp bị hack cho một phần nhỏ không xác định tập hợp con của Python repr. Đây rõ ràng là một điều xấu.
  • Ngay cả khi chương trình của bạn vẫn ở trong Python, bạn có thể thay đổi phiên bản Python. Giả sử ứng dụng của bạn bắt đầu trong Python 2; sau rất nhiều thử nghiệm bạn đã quản lý để di chuyển nó sang Python 3 - thật không may, bạn không thực sự kiểm tra tất cả mã của mình - bạn có tất cả các tệp cấu hình nằm xung quanh trên máy của khách hàng, được viết cho Python 2 và trên đó bạn không Tôi thực sự có quyền kiểm soát. Bạn thậm chí không thể cung cấp "chế độ tương thích" để đọc các tệp cấu hình cũ (thường được thực hiện cho các định dạng tệp), trừ khi bạn sẵn sàng gói / gọi trình thông dịch Python 2!
  • Ngay cả khi bạn đang ở trong Python, việc sửa đổi tệp cấu hình từ mã là một vấn đề thực sự, bởi vì ... tốt, mã sửa đổi hoàn toàn không tầm thường, đặc biệt là mã có cú pháp phong phú và không có trong LISP hoặc tương tự. Một chương trình của chúng ta có một tập tin cấu hình mà là Python, ban đầu được viết bằng tay, nhưng mà sau này hóa ra nó sẽ hữu ích để thao tác thông qua phần mềm (một thiết lập đặc biệt là một danh sách những thứ đó là cách đơn giản để sắp xếp lại sử dụng một giao diện đồ họa). Đây là một vấn đề lớn, bởi vì:

    • thậm chí chỉ thực hiện phân tích cú pháp → AST → viết lại roundtrip không phảichuyện nhỏ (bạn sẽ nhận thấy rằng một nửa các giải pháp được đề xuất sau đó được đánh dấu là "lỗi thời, không sử dụng, không hoạt động trong mọi trường hợp");
    • ngay cả khi họ làm việc, AST vẫn ở mức quá thấp; bạn thường quan tâm đến việc thao tác kết quả của các tính toán được thực hiện trong tệp, chứ không phải các bước mang đến cho nó;
    • điều này đưa chúng ta đến một thực tế đơn giản là bạn không thể chỉnh sửa các giá trị mà bạn quan tâm, bởi vì chúng có thể được tạo bởi một số tính toán phức tạp mà bạn không thể hiểu / thao tác thông qua mã của mình.

    So sánh điều này với XML JSON, INI hoặc (Chúa cấm!), Trong đó biểu diễn trong bộ nhớ luôn có thể được chỉnh sửa và ghi lại mà không mất dữ liệu (XML, trong đó hầu hết các trình phân tích cú pháp DOM có thể giữ khoảng trắng trong các nút văn bản và nút nhận xét) hoặc ít nhất là chỉ mất một số định dạng (JSON, trong đó bản thân định dạng không cho phép nhiều hơn dữ liệu thô bạn đang đọc).


Vì vậy, như thường lệ, không có giải pháp rõ ràng; chính sách hiện tại của tôi về vấn đề này là:

  • nếu tập tin cấu hình là:

    • chắc chắn cho một ứng dụng Python và riêng tư với nó - như trong, không ai khác sẽ cố đọc từ nó;
    • viết tay;
    • đến từ một nguồn đáng tin cậy;
    • sử dụng các loại dữ liệu ứng dụng mục tiêu thực sự là một phí bảo hiểm;

    một tệp Python có thể là một ý tưởng hợp lệ;

  • nếu thay vào đó:

    • có thể có khả năng có một số ứng dụng khác được đọc từ nó;
    • có khả năng tệp này có thể được chỉnh sửa bởi một ứng dụng, thậm chí có thể là chính ứng dụng của tôi;
    • được cung cấp bởi một nguồn không đáng tin cậy.

    một định dạng "chỉ dữ liệu" có thể là một ý tưởng tốt hơn.

Lưu ý rằng không bắt buộc phải thực hiện một lựa chọn duy nhất - Gần đây tôi đã viết một ứng dụng sử dụng cả hai phương pháp. Tôi có một tệp gần như không bao giờ được sửa đổi với cài đặt đầu tiên, cài đặt viết tay, trong đó có những lợi thế của việc có phần thưởng Python đẹp và tệp JSON để cấu hình được chỉnh sửa từ UI.


1
điểm rất tốt về việc tạo hoặc viết lại cấu hình! Nhưng một vài định dạng khác ngoài XML có thể giữ lại các bình luận ở đầu ra, điều mà tôi cho là cực kỳ quan trọng đối với cấu hình. Các định dạng khác đôi khi giới thiệu một note:trường bị bỏ qua cho cấu hình.
amon

2
"Nếu người dùng có thể sửa đổi tệp cấu hình, họ có thể làm bất cứ điều gì ứng dụng có thể làm" - điều này không hoàn toàn đúng. Làm thế nào về việc kiểm tra tệp cấu hình sáng bóng mà ai đó bạn không biết đã tải lên trên pastebin?
Dmitry Grigoryev

2
@DmitryGrigoryev: nếu bạn đang nhắm đến mục tiêu đó, bạn cũng có thể nói với nạn nhân của mình sao chép-dán một số curl ... | bash, điều đó thậm chí còn ít rắc rối hơn. :-P
Matteo Italia

@DmitryGrigoryev: và đó là loại điều có thể cho phép ai đó phá hỏng hoàn toàn hệ thống sản xuất trong ngày đầu tiên đi làm. Nếu 'eval' là trình phân tích cú pháp của bạn, điều đó có nghĩa là không có cơ hội kiểm tra sự cố trước khi nó được đọc. (cùng lý do tại sao shell script rất tệ trong sản xuất). INI, YAML hoặc JSON đều an toàn trong vấn đề này.
Joe

1
@DmitryGrigoryev: quan điểm của tôi là nếu loại nạn nhân của bạn đủ ngu ngốc để sao chép một cách mù quáng một tập tin cấu hình, bạn có thể lừa anh ấy / cô ấy làm bất cứ điều gì trên máy của họ bằng các phương pháp ít xiên hơn ("dán nó vào bàn điều khiển để khắc phục sự cố của bạn! "). Ngoài ra, ngay cả với các tệp cấu hình không thể thực thi cũng có nhiều nguy cơ gây hại - thậm chí chỉ trỏ độc hại khi đăng nhập vào các tệp quan trọng (nếu ứng dụng chạy với đủ đặc quyền), bạn có thể phá hoại hệ thống. Đó là lý do tại sao tôi nghĩ rằng trong thực tế, nó không có nhiều sự khác biệt trong các điều khoản bảo mật.
Matteo Italia

8

Câu hỏi chính là: bạn có muốn tệp cấu hình của mình ở một số ngôn ngữ hoàn chỉnh Turing (giống như Python) không? Nếu bạn làm muốn điều đó, bạn cũng có thể xem xét việc nhúng một số khác (Turing hoàn chỉnh) ngôn ngữ kịch bản như Guile hoặc Lua (bởi vì có thể bị coi là "đơn giản" để sử dụng, hoặc nhúng, hơn Python là; đọc chương về Mở rộng & Nhúng Python ). Tôi sẽ không thảo luận thêm (vì các câu trả lời khác của Amon - đã thảo luận sâu hơn) nhưng lưu ý rằng việc nhúng một ngôn ngữ kịch bản trong ứng dụng của bạn là một lựa chọn kiến ​​trúc chính , mà bạn nên xem xét từ rất sớm; Tôi thực sự không khuyên bạn nên đưa ra lựa chọn đó sau!

Một ví dụ nổi tiếng về một "tập lệnh" có thể cấu hình chương trình là trình soạn thảo GNU emacs (hoặc có thể là AutoCAD trong vương quốc độc quyền); vì vậy, hãy lưu ý rằng nếu bạn chấp nhận kịch bản, một số người dùng cuối cùng sẽ sử dụng - và có thể lạm dụng, theo quan điểm của bạn - cơ sở đó rộng rãi và tạo ra một kịch bản nhiều nghìn dòng; do đó việc lựa chọn một ngôn ngữ kịch bản đủ tốt là rất quan trọng.

Tuy nhiên (ít nhất là trên các hệ thống POSIX), bạn có thể xem xét thuận tiện để cho phép "tệp" cấu hình được tính toán động tại thời điểm khởi tạo (tất nhiên, để lại gánh nặng cấu hình lành mạnh cho quản trị viên hoặc người dùng hệ thống của bạn; thực sự đó là cấu hình văn bản xuất phát từ một số tập tin hoặc từ một số lệnh). Vì thế, bạn có thể đơn giản chấp nhận quy ước (và ghi lại nó) rằng một đường dẫn tệp cấu hình bắt đầu bằng ví dụ a !hoặc a |thực sự là một lệnh shell mà bạn sẽ đọc như một đường ống dẫn . Điều này khiến người dùng của bạn có quyền lựa chọn sử dụng bất kỳ "tiền xử lý" hoặc "ngôn ngữ kịch bản" nào mà anh ta quen thuộc nhất.

(bạn cần tin tưởng người dùng của mình về các vấn đề bảo mật nếu bạn chấp nhận cấu hình được tính toán động)

Vì vậy, trong mã khởi tạo của bạn, mainví dụ , bạn sẽ chấp nhận một số --config đối số confarg và nhận một số FILE*configf;từ nó. Nếu đối số đó bắt đầu bằng !(tức là nếu (confarg[0]=='!')....), bạn sẽ sử dụng configf = popen(confarg+1, "r");và đóng đường ống đó với pclose(configf);. Nếu không, bạn sẽ sử dụng configf=fopen(confarg, "r");và đóng tệp đó với fclose(configf);(đừng quên kiểm tra lỗi). Xem ống (7) , popen (3) , fopen (3) . Đối với một ứng dụng được mã hóa bằng Python, hãy đọc về os.popen , v.v ...

(tài liệu cũng dành cho người dùng kỳ lạ muốn vượt qua tệp cấu hình có tên !foo.configđể vượt qua ./!foo.configđể bỏ qua popenthủ thuật ở trên)

BTW, một mẹo như vậy chỉ là một sự tiện lợi (để tránh yêu cầu người dùng nâng cao, ví dụ mã một số tập lệnh shell để tạo tệp cấu hình ). Nếu người dùng muốn báo cáo bất kỳ lỗi nào, anh ta nên gửi cho bạn tệp cấu hình đã tạo ...

Lưu ý rằng bạn cũng có thể thiết kế ứng dụng của mình với khả năng sử dụng và tải plugin tại thời điểm khởi tạo, ví dụ như với dlopen (3) (và bạn cần tin tưởng người dùng của mình về plugin đó). Một lần nữa, đây là một quyết định kiến ​​trúc rất quan trọng (và bạn cần xác định và cung cấp một số API và quy ước khá ổn định về các plugin này và ứng dụng của bạn).

Đối với một ứng dụng được mã hóa bằng ngôn ngữ script như Python, bạn cũng có thể chấp nhận một số đối số chương trình cho eval hoặc exec hoặc các nguyên hàm tương tự. Một lần nữa, các vấn đề bảo mật sau đó là mối quan tâm của người dùng (nâng cao) .

Về định dạng văn bản cho tệp cấu hình của bạn (có được tạo hay không), tôi tin rằng bạn chủ yếu cần tài liệu tốt (và việc chọn một số định dạng cụ thể không quan trọng; tuy nhiên tôi khuyên bạn nên để người dùng của mình có thể đặt một số ý kiến ​​-kipped- bên trong nó). Bạn có thể sử dụng JSON (tốt nhất là với một số trình phân tích cú pháp JSON chấp nhận và bỏ qua các nhận xét thông thường //cho đến khi eol hoặc /*... */...), hoặc YAML, hoặc XML, hoặc INI hoặc điều của riêng bạn. Phân tích tệp cấu hình khá dễ dàng (và bạn sẽ tìm thấy nhiều thư viện liên quan đến tác vụ đó).


+1 để đề cập đến tính đầy đủ của Turing trong các ngôn ngữ lập trình. Một số công việc thú vị tiết lộ rằng việc giới hạn sức mạnh tính toán của định dạng đầu vào là chìa khóa để đảm bảo lớp xử lý đầu vào. Sử dụng ngôn ngữ lập trình Turing-Complete đi theo hướng ngược lại.
Matheus Moreira

2

Thêm vào câu trả lời của amon , bạn đã xem xét lựa chọn thay thế chưa? JSON có thể nhiều hơn bạn cần, nhưng các tệp Python có thể sẽ cung cấp cho bạn các vấn đề trong tương lai vì các lý do được đề cập ở trên.

Tuy nhiên Python đã có một trình phân tích cú pháp cấu hình cho một ngôn ngữ cấu hình rất đơn giản có thể đáp ứng mọi nhu cầu của bạn. Các ConfigParsermô-đun thực hiện một ngôn ngữ cấu hình đơn giản.


1
Sử dụng một cái gì đó "tương tự như ... các tệp Microsoft Windows INI" dường như là một ý tưởng tồi, cả hai đều cho rằng đó không phải là một định dạng đặc biệt linh hoạt và bởi vì "tương tự" ngụ ý không tương thích không có giấy tờ.
Pete Kirkham

1
@PeteKirkham Chà, thật đơn giản, nó được ghi lại và nó là một phần của thư viện chuẩn Python. Nó có thể là giải pháp hoàn hảo cho nhu cầu của OP, bởi vì anh ấy đang tìm kiếm thứ gì đó được Python hỗ trợ trực tiếp và đơn giản hơn JSON. Miễn là anh ấy không xác định rõ hơn nhu cầu của anh ấy là gì, tôi nghĩ câu trả lời này có thể hữu ích cho anh ấy.
CodeMonkey

1
Tôi sẽ đề xuất về cơ bản điều này - xem các loại tệp cấu hình Python libs hỗ trợ và chọn một trong số đó. Ngoài ra, Powershell có khái niệm về các phần dữ liệu - cho phép các cấu trúc ngôn ngữ Powershell giới hạn - bảo vệ chống lại mã độc. Nếu Python có lib hỗ trợ một tập hợp con Python giới hạn cho cấu hình, thì ít nhất nó sẽ giảm thiểu một trong những nhược điểm chống lại ý tưởng trong OP.
Χpẘ

1
@PeteKirkham Nhiều khả năng là một vấn đề theo cách khác. Windows có xu hướng có một loạt các crap không có giấy tờ phát nổ trên bạn. Python có xu hướng được ghi chép tốt và đơn giản. Điều đó nói rằng, nếu tất cả những gì bạn cần là các cặp khóa / giá trị đơn giản ( có thể có các phần), thì đó là một lựa chọn khá tốt. Tôi nghi ngờ điều này bao gồm 90% các trường hợp sử dụng. Nếu các tệp cấu hình của .NET là ini thay vì XML quái dị với một lược đồ thực sự mã hóa thành cấu hình, thì tất cả chúng ta sẽ tốt hơn rất nhiều.
jpmc26

1
@PeteKirkham Không thực sự. INI là tốt nhất cho các trường hợp sử dụng đơn giản ở nơi đầu tiên, rất có thể bạn có thể tránh mọi sự không tương thích. Chúng cũng không quan trọng nếu bạn không sử dụng tệp với hai ngôn ngữ khác nhau và ngay cả khi bạn là ai, bạn có thể tìm thấy các triển khai mở trong bất kỳ ngôn ngữ nào (cho phép bạn không có sự không tương thích hoặc, tối thiểu, biết chính xác những gì họ đang). Tôi đồng ý rằng bạn nên sử dụng định dạng khác nếu trường hợp sử dụng của bạn thực sự đủ phức tạp để bạn bắt đầu chạy vào chúng hoặc nếu bạn không thể tìm thấy một triển khai hiện có mà bạn có thể tin tưởng, nhưng điều đó không phổ biến.
jpmc26

1

Tôi đã làm việc trong một thời gian dài với một số phần mềm nổi tiếng có các tệp cấu hình được viết bằng TCL, vì vậy ý ​​tưởng này không mới. Điều này hoạt động khá tốt, vì người dùng không biết ngôn ngữ vẫn có thể viết / chỉnh sửa các tệp cấu hình đơn giản bằng một set name valuecâu lệnh, trong khi người dùng và nhà phát triển nâng cao hơn có thể thực hiện các thủ thuật tinh vi với điều này.

Tôi không nghĩ rằng "các tệp cấu hình có thể khó gỡ lỗi" là một mối quan tâm hợp lệ. Miễn là ứng dụng của bạn không bắt buộc người dùng phải viết các tập lệnh, người dùng của bạn luôn có thể sử dụng các bài tập đơn giản trong các tệp cấu hình của họ, điều này khó có thể trở nên khó khăn hơn so với JSON hoặc XML.

Viết lại cấu hình là một vấn đề, mặc dù nó không tệ như nó có vẻ. Cập nhật mã tùy ý là không thể, nhưng tải cấu hình từ một tệp, thay đổi nó và lưu lại là được. Về cơ bản, nếu bạn thực hiện một số tập lệnh trong tệp cấu hình không chỉ đọc, bạn sẽ kết thúc với một danh sách các set name valuecâu lệnh tương đương sau khi được lưu. Một gợi ý hay rằng điều này sẽ xảy ra là một bình luận "không chỉnh sửa" ở đầu tập tin.

Một điều cần xem xét là các tệp cấu hình của bạn sẽ không thể đọc được một cách đáng tin cậy bằng các công cụ dựa trên regex đơn giản sed, nhưng theo tôi hiểu thì đây không phải là trường hợp với các tệp JSON hiện tại của bạn, vì vậy sẽ không mất nhiều.

Chỉ cần đảm bảo bạn sử dụng các kỹ thuật hộp cát thích hợp khi thực hiện các tệp cấu hình của mình.


1
"Phần mềm" là một danh từ không đếm được, vì vậy nó phải là " một số phần mềm nổi tiếng."
jpmc26

1

Bên cạnh tất cả các điểm hợp lệ của các câu trả lời hay khác ở đây (wow, họ thậm chí đã đề cập đến khái niệm Turing-Complete), thực sự có một vài lý do thực tế vững chắc để KHÔNG sử dụng tệp Python làm cấu hình của bạn, ngay cả khi bạn đang làm việc trên Python- Dự án duy nhất.

  1. Các cài đặt bên trong tệp nguồn Python về mặt kỹ thuật là một phần của mã nguồn thực thi, thay vì tệp dữ liệu chỉ đọc. Nếu bạn đi theo con đường này, bạn thường sẽ làm import config, bởi vì loại "tiện lợi" đó có lẽ là một trong những lý do chính khiến mọi người bắt đầu sử dụng tệp Python làm cấu hình ở vị trí đầu tiên. Bây giờ bạn có xu hướng cam kết cấu hình đó vào repo của mình, nếu không, người dùng cuối của bạn sẽ gặp phải một ImportError khó hiểu khi họ cố gắng chạy chương trình của bạn lần đầu tiên.

  2. Giả sử bạn thực sự cam kết cấu hình đó vào repo của mình, bây giờ các thành viên trong nhóm của bạn có thể có các cài đặt khác nhau trên môi trường khác nhau. Hãy tưởng tượng một ngày nào đó một số thành viên vô tình cam kết tập tin cấu hình cục bộ của mình vào repo.

  3. Cuối cùng nhưng không kém phần quan trọng, dự án của bạn có thể có mật khẩu trong tệp cấu hình. (Đây là một cách thực hành gây tranh cãi, nhưng dù sao nó cũng xảy ra.) Và nếu tệp cấu hình của bạn tồn tại trong repo, bạn có nguy cơ cam kết thông tin đăng nhập của mình thành repo công khai.

Bây giờ, bằng cách sử dụng tệp cấu hình chỉ có dữ liệu, chẳng hạn như định dạng JSON phổ quát, có thể tránh được tất cả 3 vấn đề ở trên, vì bạn có thể yêu cầu người dùng đưa ra config.json của riêng họ và đưa nó vào chương trình của bạn.

PS: Đúng là JSON có nhiều hạn chế. 2 trong số những hạn chế được đề cập bởi OP, có thể được giải quyết bằng một số sáng tạo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.