From ehuelsmann at common-lisp.net Sun Jan 6 20:59:07 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Sun, 6 Jan 2008 15:59:07 -0500 (EST) Subject: [py-configparser-cvs] r1 - branches developer-resources public_html tags trunk Message-ID: <20080106205907.03AD8830AD@common-lisp.net> Author: ehuelsmann Date: Sun Jan 6 15:59:07 2008 New Revision: 1 Added: branches/ developer-resources/ public_html/ tags/ trunk/ trunk/config.lisp trunk/package.lisp trunk/parser.lisp trunk/py-configparser.asd Log: Added: trunk/config.lisp ============================================================================== --- (empty file) +++ trunk/config.lisp Sun Jan 6 15:59:07 2008 @@ -0,0 +1,293 @@ + +(cl:in-package :py-configparser) + +;; The conditions (errors) + +(define-condition configparser-error (error) ()) + +;; Errors for the configuration management side +(define-condition config-error (configparser-error) ()) +(define-condition no-section-error (config-error) ()) +(define-condition duplicate-section-error (config-error) ()) +(define-condition no-option-error (config-error) ()) +(define-condition interpolation-error (config-error) ()) +(define-condition interpolation-depth-error (interpolation-error) ()) +(define-condition interpolation-missing-option-error (interpolation-error) ()) +(define-condition interpolation-syntax-error (interpolation-error) ()) + + +;; +;; Configuration storage and management routines +;; + + +;; The structures +;; Note: because ABCL has issues with its CLOS support +;; (as per 1-1-2008), we use structures below to +;; be maximally portable. + + +(defstruct section + name + options) + +(defstruct config + (defaults (make-section :name "DEFAULT")) + sections + (option-name-transform-fn #'string-downcase) + (section-name-transform-fn #'identity)) + +(defun norm-option-name (config option-name) + (funcall (config-option-name-transform-fn config) option-name)) + +(defun norm-section-name (config section-name) + (funcall (config-section-name-transform-fn config) section-name)) + +(defun %validate-section-name (name) + (when (or (= 0 (length name)) + (find #\] name) + (find #\Newline name) + (find #\Return name)) + (error 'no-section-error)) ;; Invalid section name, signal so. + name) + +(defun %validate-option-name (name) + (when (or (= 0 (length name)) + (eql (aref name 0) #\[) + (find #\Space name) + (find #\Tab name) + (find #\Return name) + (find #\Newline name)) + (error 'no-option-error));; No such option error + name) + +;; non-API +(defun %get-section (config section-name) + (if (string= "DEFAULT" section-name) + (config-defaults config) + (let* ((norm-section-name (norm-section-name config section-name)) + (section (find norm-section-name (config-sections config) + :key #'section-name + :test #'string=))) + (unless section + (error 'no-section-error)) ;; no-such-section error + section))) + +;; non-API +(defun %get-option (config section-name option-name if-does-not-exist) + (let* ((section (%get-section config section-name)) + (norm-option (norm-option-name config option-name)) + (option (assoc norm-option + (section-options section) + :test #'string=))) + (if (null option) + (if (eq if-does-not-exist :error) + (error 'no-option-error) ;; no such option error + (values (car (push (list (%validate-option-name option-name)) + (section-options section))) + section)) + (values option section)))) + +;; +;; The API +;; + +(defun defaults (config) + "Returns an alist containing instance wide defaults, where the +elements are 2-element dotted lists: the CDR is the value +associated with the key." + (section-options (config-defaults config))) + +(defun sections (config) + "Returns a list of names of defined sections." + (mapcar #'section-name (config-sections config))) + +(defun has-section-p (config section-name) + "Returns `NIL' when the section is not added to the config yet, +some other value if it is." + (handler-case + (%get-section config section-name) + (no-section-error () nil))) + +(defun add-section (config section-name) + "Adds a new section to the config. + +If the section exists, the `duplicate-section-error' is raised." + (%validate-section-name section-name) + (let ((norm-section-name (funcall (config-section-name-transform-fn config) + section-name))) + (when (has-section-p config section-name) + (error 'duplicate-section-error)) + (car (push (make-section :name norm-section-name) + (config-sections config))))) + +(defun options (config section-name) + "Returns a list of option names which are defined in the given section." + (let ((section (%get-section config section-name))) + (mapcar #'first (section-options section)))) + +(defun has-option-p (config section-name option-name) + "Returns a generalised boolean with a value of `NIL' when +the specified option does not exist in the specified section +and some other value otherwise." + (handler-case + (%get-option config section-name option-name :error) + (no-option-error () nil))) + +;; non-API +(defun %extract-replacement (option-value) + ;; Returns: (VALUES replacement-option start end) or NIL + (let ((%-pos (position #\% option-value))) + (when (and %-pos + (< (+ 3 %-pos) (length option-value)) + (eql (aref option-value (1+ %-pos)) #\( )) + (let ((paren-pos (position #\) option-value :start %-pos))) + (unless (and paren-pos + (< (1+ paren-pos) (length option-value)) + (eql (aref option-value (1+ paren-pos)) #\s)) + (error 'interpolation-syntax-error)) + ;; syntax error: %(..)s is minimally required + (when (<= 0 (- paren-pos %-pos 2)) + (let ((replacement-name + (make-array (- paren-pos %-pos 2) + :element-type (array-element-type option-value) + :displaced-to option-value + :displaced-index-offset (+ 2 %-pos)))) + (when (= 0 (length replacement-name)) + ;; some preconditions on replacement-name + (error 'interpolation-syntax-error)) + (values replacement-name %-pos (1+ paren-pos)))))))) + +;; non-API +(defun %option-value (config section option-name &key defaults) + (if (string= option-name "__name__") + (section-name section) + (let* ((norm-option-name (norm-option-name config option-name))) + (labels ((get-value (repositories) + (when (null repositories) + (error 'interpolation-missing-option-error)) + ;; no such option error + (let ((option (has-option-p config (section-name section) + option-name))) + (if option + (cdr option) + (get-value (cdr repositories)))))) + (get-value (list (section-options section) + defaults + (defaults config))))))) + +;; non-API +(defun %expand-option-value (config section option-value defaults + &optional dependees) + (multiple-value-bind + (replacement-name start end) + (%extract-replacement option-value) + (unless replacement-name + ;; nothing to do here... + (return-from %expand-option-value option-value)) + + (let ((norm-replacement (norm-option-name config replacement-name)) + (replacement-value (%option-value config section + replacement-name + :defaults defaults))) + (when (member norm-replacement dependees :test #'string=) + (error 'interpolation-depth-error)) ;; recursive dependency... + (%expand-option-value + config + section + (concatenate 'string + (subseq option-value 0 start) + (%expand-option-value config + section + replacement-value + defaults + (cons norm-replacement dependees)) + (subseq option-value (1+ end) (length option-value))) + defaults + dependees)))) + +(defun get-option (config section-name option-name + &key (expand t) defaults type) + "Returns the value of the specified option in the specified section. + +If `expand' is `NIL', any options which depend on other options +won't be expanded and the raw configuration value is returned. + +When `defaults' is an alist of which the elements are dotted lists of +key/value pairs, these values are used in the expansion of option values. + +`type' may be one of `:boolean', `:number' or it may remain unspecified." + (multiple-value-bind + (option section) + (%get-option config section-name option-name :error) + (flet ((convert-boolean (v) + (cond + ((member v '("1" "yes" "true" "on") :test #'string=) + T) + ((member v '("0" "no" "false" "off") :test #'string=) + NIL) + (t + (error 'not-a-boolean)))) + (convert-number (v) + (parse-number:parse-number v))) + (let ((string-value + (if expand + (%expand-option-value config + section (cdr option) + (list option-name)) + (cdr option)))) + (cond + ((eq type :boolean) + (convert-boolean string-value)) + ((eq type :number) + (convert-number string-value)) + ((null type) + string-value) + (t + (error "Illegal `type' parameter value."))))))) + +(defun set-option (config section-name option-name value) + "Sets the value of the specified option in the specified section. + +If the section does not exist, a `no-section-error' is raised. If the +option does not exist, it is created." + (let ((option (%get-option config section-name option-name :create))) + (setf (cdr option) value))) + +(defun items (config section-name &key (expand t) defaults) + "Returns an alist of which the items are dotted lists of key/value +pairs being the option names and values specified in the given section. + +When `expand' is `NIL', options are returned in raw form. Otherwise +option values are expanded. + +The definition of `defaults' is the same as for `get-option'." + (let ((section (get-section config section-name))) + (if expand + (mapcar #'(lambda (x) + (cons (car x) (get-option p section-name + (cdr x) ;; option-name + :expand t + :defaults defaults))) + (section-options section)) + (section-options section)))) + +(defun remove-option (config section-name option-name) + "Remove the specified option from the given section." + (multiple-value-bind + (option section) + (%get-option config section-name option-name :error) + (setf (section-options section) + (remove option (section-options section))))) + +(defun remove-section (config section-name) + "Remove the specified section. + +In case the section name equals the magic name `DEFAULT', +an error is raised, since this section can't be removed." + (when (string= section-name "DEFAULT") + (error 'no-section-error)) ;; no such section error + (let ((section (%get-section config section-name))) + (setf (config-sections config) + (remove section (config-sections config))))) + Added: trunk/package.lisp ============================================================================== --- (empty file) +++ trunk/package.lisp Sun Jan 6 15:59:07 2008 @@ -0,0 +1,54 @@ + +;; This package is actuall two things: +;; 1) a configuration management utility +;; 2) a configuration file parser/writer in the .INI format +;; +;; But in the Python module this distinction hasn't been implemented +;; this stringently, meaning we're stuck to the current naming scheme. + +;; There's no reason however that you can't create your own format +;; and parse that, storing it in the config object as defined in this +;; package. (However, if you already use this module, you might as well +;; use the INI format as persistent format.) + + +(cl:defpackage #:py-configparser + (:use #:cl) + (:export + ;; common condition class + #:configparser-error + + ;; Configuration management + ;; Error classes + #:no-section-erorr + #:duplicate-section-error + #:no-option-error + #:interpolation-error + #:interpolation-depth-error + #:interpolation-missing-option-error + #:interpolation-syntax-error + + ;; Functions + #:make-config + #:defaults + #:sections + #:has-section-p + #:add-section + #:options + #:has-option-p + #:get-option + #:set-option + #:items + #:remove-option + #:remove-section + + ;; Configuration file parsing + ;; Error classes + #:parsing-error + #:missing-section-header-error + + ;; Functions + #:read-stream + #:read-files + #:write-stream)) + Added: trunk/parser.lisp ============================================================================== --- (empty file) +++ trunk/parser.lisp Sun Jan 6 15:59:07 2008 @@ -0,0 +1,216 @@ + +(cl:in-package #:py-configparser) + +;; Errors for the parsing side + +(define-condition parsing-error (configparser-error) ()) +(define-condition missing-section-header-error (parsing-error) ()) + + + +;; The reader + +(proclaim '(special *line-no* *current-section* *file-name* + *current-input*)) +(proclaim '(inline %read-char %unread-char)) + +(defun %read-char (stream) + (let ((ch (read-char stream nil :eof))) + (when (eql ch #\Newline) + (incf *line-no*)) + (if (eq ch :eof) #\Newline ch))) + +(defun ensure-section (config section-name) + (handler-case + (%get-section config section-name) + (no-section-error () + (add-section config section-name))) + section-name) + +(defun is-whitespace (c) + (or (eq c #\Space) + (eq c #\Tab) + (eq c #\Return))) + +(defun is-comment-char (c) + (or (eq c #\;) + (eq c #\#))) + +(defun skip-whitespace (s) + (loop for c = (%read-char s) + while (is-whitespace c))) + +(defun skip-emtpy-line (s) + (loop for c = (%read-char s) + if (eq c #\Newline) do (return) + else unless (is-whitespace c) + do (error 'parsing-error))) ;; empty line expected + +(defun skip-to-eol (s) + (loop for c = (%read-char s) + until (eq c #\Newline))) + +(defun expect-char (s expect &key skip-whitespace) + (let ((ch (%read-char s))) + (when (and skip-whitespace + (is-whitespace ch)) + (loop for c = (%read-char s) + while (is-whitespace c) + finally (setf ch c))) + (unless (eq ch expect) + (error 'parsing-error)) ;; character expect expected, but ch found + ch)) + +(defun expect-one-of (s expect-bag &key skip-whitespace) + (let ((ch (%read-char s))) + (when (and skip-whitespace + (is-whitespace ch)) + (loop for c = (%read-char s) + while (is-whitespace c) + finally (setf ch c))) + (unless (member ch expect-bag) + (error 'parsing-error)) ;; character ch found, but looking for EXPECT-BAG + ch)) + +(defun make-input-buffer (p) + (make-array 20 :element-type 'cl:character :fill-pointer 0)) + +(proclaim '(inline extend-input)) +(defun extend-input (p c) + (vector-push-extend c *current-input* 20)) + +(defun finalize-input (p) + (let ((cp *current-input*)) + (setf *current-input* + (make-input-buffer p)) + cp)) + +(defun read-section-name (p s) + (expect-char s #\[) + (loop for c = (%read-char s) + if (eq c #\Newline) + do (error 'parsing-error) ;; we can't have newlines in section names! + else if (eq c #\]) + do (progn + (skip-to-eol s) + (return (finalize-input p))) + else do (extend-input p c))) + +(defun read-option-name (p s) + (loop for c = (%read-char s) + if (or (eq c #\:) + (eq c #\=)) + do (let ((option-name (finalize-input p))) + (when (= 0 (length option-name)) + (error 'parsing-error)) ;; No option name found + (return option-name)) + else if (is-whitespace c) + do (unread-char (expect-one-of s '(#\: #\=) :skip-whitespace t) s) + else do (extend-input p c))) + +(defun read-option-value (p s &key (leading-white :skip)) + (let ((leading-mode t) + (lead-detected nil)) + (loop for c = (%read-char s) + unless (or (eql c #\Return) + (eql c #\Newline)) + do (if (and leading-mode + (is-whitespace c)) + (setf lead-detected t) + (progn + (when (and (eq leading-white :fold) + leading-mode + lead-detected) + (extend-input p #\Space)) + (setf leading-mode nil) + (extend-input p c))) + + if (and (eql c #\Newline) + (let ((ch (peek-char nil s nil nil))) + (or (eql ch #\Space) + (eql ch #\Tab)))) + do (return (read-option-value p s :leading-white :fold)) + until (eql c #\Newline) + finally (return (finalize-input p))))) + +(defun reading-driver (p s) + (let ((*line-no* 0) + (*current-section* nil) + (*current-input* (make-input-buffer p))) + (loop for c = (peek-char nil s nil :eof) + until (eq c :eof) + if (eql c #\[) + do (setf *current-section* + (ensure-section p (read-section-name p s))) + + else if (is-whitespace c) + do (skip-empty-line s) + + else if (is-comment-char c) + do (skip-to-eol s) + + else if (eql c #\Newline) + do (%read-char s) ;; skip over the newline character + + else do (if (null *current-section*) + (error 'missing-section-header-error) + (set-option p + *current-section* + (read-option-name p s) + (read-option-value p s)))))) + +;; +;; The API +;; + +(defun read-files (config filenames) + "Parses the files given in the list `filenames', if they exist. +The list is processed first to last, overwriting any pre-existing +values with the last value read. + +The results are stored in `config' which is modified destructively. + +Returns as values the configuration and the list of files actually read." + (let (files-read) + (dolist (filename (mapcar #'probe-file filenames) + (values config files-read)) + (with-open-file (s filename + :direction :input + :if-does-not-exist :error) + (read-stream config s :stream-name filename)) + (push filename files-read)))) + +(defun read-stream (config stream &key (stream-name "an unknown stream")) + "Parses the content of `stream' as a configuration file, +storing any values in `config' which is modified destructively. + +This function maps from the python 'readfp()' function." + (let ((*file-name* stream-name)) + (reading-driver config stream) + config ) + +(defun %format-value (value) + (if (and (numberp value) + (not (integerp value))) + (format nil "~,,,,,,'eE" value) + value)) + +(defun write-stream (config stream) + "Writes the configuration file corresponding to the +in-memory config state. Reloading the file +with `read-stream' or `read-files' will restore config state." + (flet ((write-section (section) + (format stream "[~a]~%" (section-name section)) + (format stream "~:{~A = ~{~A~%~}~}~%" + (mapcar #'(lambda (option) + (list (car option) + (list (%format-value (cdr option))))) + (section-options section))))) + (let ((*print-radix* nil) + (*print-base* 10)) + ;; set the printer output as expected by python + (when (defaults config) + ;; write the defaults too!! + (write-section (config-defaults config))) + (mapcar #'write-section (config-sections config))))) + Added: trunk/py-configparser.asd ============================================================================== --- (empty file) +++ trunk/py-configparser.asd Sun Jan 6 15:59:07 2008 @@ -0,0 +1,20 @@ + + +(in-package #:cl-user) + +(defpackage #:py-configparser-system + (:use #:cl #:asdf)) + +(in-package #:py-configparser-system) + +(defsystem py-configparser + :name "py-configparser" + :author "Erik Huelsmann" + :version "1.0-dev" + :license "MIT" + :description "Common Lisp implementation of the Python ConfigParser module" + :depends-on (#:parse-number) + :components ((:file "package") + (:file "config" :depends-on ("package")) + (:file "parser" :depends-on ("config")))) + From ehuelsmann at common-lisp.net Sun Jan 6 21:01:23 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Sun, 6 Jan 2008 16:01:23 -0500 (EST) Subject: [py-configparser-cvs] r2 - public_html Message-ID: <20080106210123.EB4F156245@common-lisp.net> Author: ehuelsmann Date: Sun Jan 6 16:01:23 2008 New Revision: 2 Added: public_html/index.shtml public_html/project-name public_html/style.css Log: Add current website. Added: public_html/index.shtml ============================================================================== --- (empty file) +++ public_html/index.shtml Sun Jan 6 16:01:23 2008 @@ -0,0 +1,25 @@ + + + + + <!--#include virtual="project-name" --> + + + + + +
+

+
+ +

This is an automatically generated placeholder page: this project + has not yet created a website.

+ +

Back to Common-lisp.net.

+ +
+ Valid XHTML 1.0 Strict +
+ + Added: public_html/project-name ============================================================================== --- (empty file) +++ public_html/project-name Sun Jan 6 16:01:23 2008 @@ -0,0 +1 @@ +py-configparser Added: public_html/style.css ============================================================================== --- (empty file) +++ public_html/style.css Sun Jan 6 16:01:23 2008 @@ -0,0 +1,54 @@ + +.header { + font-size: medium; + background-color:#336699; + color:#ffffff; + border-style:solid; + border-width: 5px; + border-color:#002244; + padding: 1mm 1mm 1mm 5mm; +} + +.footer { + font-size: small; + font-style: italic; + text-align: right; + background-color:#336699; + color:#ffffff; + border-style:solid; + border-width: 2px; + border-color:#002244; + padding: 1mm 1mm 1mm 1mm; +} + +.footer a:link { + font-weight:bold; + color:#ffffff; + text-decoration:underline; +} + +.footer a:visited { + font-weight:bold; + color:#ffffff; + text-decoration:underline; +} + +.footer a:hover { + font-weight:bold; + color:#002244; + text-decoration:underline; } + +.check {font-size: x-small; + text-align:right;} + +.check a:link { font-weight:bold; + color:#a0a0ff; + text-decoration:underline; } + +.check a:visited { font-weight:bold; + color:#a0a0ff; + text-decoration:underline; } + +.check a:hover { font-weight:bold; + color:#000000; + text-decoration:underline; } From ehuelsmann at common-lisp.net Sun Jan 6 21:31:28 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Sun, 6 Jan 2008 16:31:28 -0500 (EST) Subject: [py-configparser-cvs] r3 - public_html Message-ID: <20080106213128.D1990111CE@common-lisp.net> Author: ehuelsmann Date: Sun Jan 6 16:31:28 2008 New Revision: 3 Modified: public_html/index.shtml Log: Update the website. Modified: public_html/index.shtml ============================================================================== --- public_html/index.shtml (original) +++ public_html/index.shtml Sun Jan 6 16:31:28 2008 @@ -13,10 +13,48 @@

-

This is an automatically generated placeholder page: this project - has not yet created a website.

+

Project description

+

The py-configparser package implements the + + ConfigParser Python module functionality in Common Lisp.

-

Back to Common-lisp.net.

+

In short, it implements reading and writing of .INI-file style + configuration files with sections containing key/value pairs of + configuration options. In line with the functionalities in the python + module, does this package implement basic interpolation of option values + in other options. +

+ +

The main reason for starting the project is to be able to share + configuration files between Python and Common Lisp. Other projects + may be using the python/INI file style files too and the project is + by no means limited to the ConfigParser functionality. So, if you + have any wishes and/or nice extentions based on this format, please + send your contributions or extention requests.

+ +

Examples:

+
+
+[my-section]
+option1 = value1
+option2: value2
+
+[interpolated-section]
+option1 = value
+option2 = value2 and %(option1)s
+# this is a comment; the above line evaluates to "value2 and value"
+
+# empty lines and comments are skipped
+ 
+ + +

Releases

+

Having recently started (Christmas hacking, 2007), the project doesn't +have any official releases yet. The code however should be useable and +the only reason there's no release is because there are no unit tests yet. +(Yea, working on it....)

+ +
Valid XHTML 1.0 Strict From ehuelsmann at common-lisp.net Tue Jan 15 20:04:30 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Tue, 15 Jan 2008 15:04:30 -0500 (EST) Subject: [py-configparser-cvs] r4 - trunk Message-ID: <20080115200430.7F45D3001D@common-lisp.net> Author: ehuelsmann Date: Tue Jan 15 15:04:27 2008 New Revision: 4 Modified: trunk/config.lisp (props changed) trunk/package.lisp (props changed) trunk/parser.lisp (props changed) trunk/py-configparser.asd (props changed) Log: Add keyword properties. From ehuelsmann at common-lisp.net Tue Jan 15 20:50:42 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Tue, 15 Jan 2008 15:50:42 -0500 (EST) Subject: [py-configparser-cvs] r5 - trunk Message-ID: <20080115205042.C30E88100F@common-lisp.net> Author: ehuelsmann Date: Tue Jan 15 15:50:38 2008 New Revision: 5 Added: trunk/LICENSE (contents, props changed) trunk/README (contents, props changed) Log: Commit LICENSE and preliminary README files. Added: trunk/LICENSE ============================================================================== --- (empty file) +++ trunk/LICENSE Tue Jan 15 15:50:38 2008 @@ -0,0 +1,23 @@ +(This is the MIT / X Consortium license as taken from + http://www.opensource.org/licenses/mit-license.html) + +Copyright (c) 2008 Erik Huelsmann + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Added: trunk/README ============================================================================== --- (empty file) +++ trunk/README Tue Jan 15 15:50:38 2008 @@ -0,0 +1,3 @@ + + + From ehuelsmann at common-lisp.net Tue Jan 15 22:24:58 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Tue, 15 Jan 2008 17:24:58 -0500 (EST) Subject: [py-configparser-cvs] r6 - trunk Message-ID: <20080115222458.49B863001E@common-lisp.net> Author: ehuelsmann Date: Tue Jan 15 17:24:58 2008 New Revision: 6 Modified: trunk/parser.lisp Log: Add missing paren. Modified: trunk/parser.lisp ============================================================================== --- trunk/parser.lisp (original) +++ trunk/parser.lisp Tue Jan 15 17:24:58 2008 @@ -187,7 +187,7 @@ This function maps from the python 'readfp()' function." (let ((*file-name* stream-name)) (reading-driver config stream) - config ) + config)) (defun %format-value (value) (if (and (numberp value) From ehuelsmann at common-lisp.net Wed Jan 16 21:16:33 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Wed, 16 Jan 2008 16:16:33 -0500 (EST) Subject: [py-configparser-cvs] r7 - trunk Message-ID: <20080116211633.234F6610E7@common-lisp.net> Author: ehuelsmann Date: Wed Jan 16 16:16:32 2008 New Revision: 7 Modified: trunk/package.lisp trunk/parser.lisp Log: package.lisp: Export 'CONFIG as it is the type of the created configuration. parser.lisp: Create adjustable strings. Modified: trunk/package.lisp ============================================================================== --- trunk/package.lisp (original) +++ trunk/package.lisp Wed Jan 16 16:16:32 2008 @@ -18,6 +18,9 @@ ;; common condition class #:configparser-error + ;; configuration storage type + #:config + ;; Configuration management ;; Error classes #:no-section-erorr Modified: trunk/parser.lisp ============================================================================== --- trunk/parser.lisp (original) +++ trunk/parser.lisp Wed Jan 16 16:16:32 2008 @@ -73,7 +73,9 @@ ch)) (defun make-input-buffer (p) - (make-array 20 :element-type 'cl:character :fill-pointer 0)) + (declare (ignore p)) + (make-array 20 :element-type 'cl:character :fill-pointer 0 + :adjustable t)) (proclaim '(inline extend-input)) (defun extend-input (p c) From ehuelsmann at common-lisp.net Thu Jan 17 22:39:31 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Thu, 17 Jan 2008 17:39:31 -0500 (EST) Subject: [py-configparser-cvs] r8 - trunk Message-ID: <20080117223931.CE1F650083@common-lisp.net> Author: ehuelsmann Date: Thu Jan 17 17:39:27 2008 New Revision: 8 Modified: trunk/config.lisp Log: Fix ITEMS; found while writing tests. Modified: trunk/config.lisp ============================================================================== --- trunk/config.lisp (original) +++ trunk/config.lisp Thu Jan 17 17:39:27 2008 @@ -262,11 +262,11 @@ option values are expanded. The definition of `defaults' is the same as for `get-option'." - (let ((section (get-section config section-name))) + (let ((section (%get-section config section-name))) (if expand (mapcar #'(lambda (x) - (cons (car x) (get-option p section-name - (cdr x) ;; option-name + (cons (car x) (get-option config section-name + (car x) ;; option-name :expand t :defaults defaults))) (section-options section)) From ehuelsmann at common-lisp.net Fri Jan 18 22:59:10 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Fri, 18 Jan 2008 17:59:10 -0500 (EST) Subject: [py-configparser-cvs] r9 - trunk Message-ID: <20080118225910.31C4981002@common-lisp.net> Author: ehuelsmann Date: Fri Jan 18 17:59:09 2008 New Revision: 9 Modified: trunk/config.lisp Log: Fix issues found while writing tests: * GET-OPTION: don't forget to pass on DEFAULTS * %OPTION-VALUE: start doing something sensible Modified: trunk/config.lisp ============================================================================== --- trunk/config.lisp (original) +++ trunk/config.lisp Fri Jan 18 17:59:09 2008 @@ -162,19 +162,22 @@ (defun %option-value (config section option-name &key defaults) (if (string= option-name "__name__") (section-name section) - (let* ((norm-option-name (norm-option-name config option-name))) - (labels ((get-value (repositories) - (when (null repositories) - (error 'interpolation-missing-option-error)) - ;; no such option error - (let ((option (has-option-p config (section-name section) - option-name))) - (if option - (cdr option) + (let* ((norm-option-name (norm-option-name config option-name)) + (option (has-option-p config (section-name section) option-name))) + (if option + (cdr option) + (labels ((get-value (repositories) + (when (null repositories) + (error 'interpolation-missing-option-error)) + ;; no such option error + (let ((value (assoc norm-option-name (car repositories) + :test #'string=))) + (if value + (cdr value) (get-value (cdr repositories)))))) (get-value (list (section-options section) defaults - (defaults config))))))) + (defaults config)))))))) ;; non-API (defun %expand-option-value (config section option-value defaults @@ -234,6 +237,7 @@ (if expand (%expand-option-value config section (cdr option) + defaults (list option-name)) (cdr option)))) (cond From ehuelsmann at common-lisp.net Fri Jan 18 23:06:23 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Fri, 18 Jan 2008 18:06:23 -0500 (EST) Subject: [py-configparser-cvs] r10 - public_html Message-ID: <20080118230623.D0044586D6@common-lisp.net> Author: ehuelsmann Date: Fri Jan 18 18:06:21 2008 New Revision: 10 Modified: public_html/index.shtml (props changed) Log: Set svn:keywords. From ehuelsmann at common-lisp.net Sat Jan 19 13:57:31 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Sat, 19 Jan 2008 08:57:31 -0500 (EST) Subject: [py-configparser-cvs] r11 - trunk/tests Message-ID: <20080119135731.553496A03A@common-lisp.net> Author: ehuelsmann Date: Sat Jan 19 08:57:30 2008 New Revision: 11 Added: trunk/tests/ trunk/tests/py-configparser-tests.asd trunk/tests/tests.lisp Log: Add tests. Added: trunk/tests/py-configparser-tests.asd ============================================================================== --- (empty file) +++ trunk/tests/py-configparser-tests.asd Sat Jan 19 08:57:30 2008 @@ -0,0 +1,17 @@ + + +(in-package #:cl-user) + +(defpackage #:py-configparser-tests-system + (:use #:cl #:asdf)) + +(in-package #:py-configparser-tests-system) + +(defsystem py-configparser-tests + :name "py-configparser-tests" + :author "Erik Huelsmann" + :version "1.0-dev" + :license "MIT" + :description "Tests for 'Common Lisp implementation of the Python ConfigParser module'" + :depends-on (#:py-configparser) + :components ((:file "tests"))) Added: trunk/tests/tests.lisp ============================================================================== --- (empty file) +++ trunk/tests/tests.lisp Sat Jan 19 08:57:30 2008 @@ -0,0 +1,304 @@ + +(defpackage #:py-configparser-tests + (:use (#:cl #:py-configparser #:rt))) + +(in-package :py-configparser-tests) + +;; test 1 +;; should succeed +(deftest basic.parser + (typep (with-input-from-string (s "[n] +p=q +z=%(p)s +") + (read-stream (make-config) s)) 'config) + T) + +(deftest basic.get-option.1 + (with-input-from-string (s "[n] +p=q +z=%(p)s + and some more +") + (equal (get-option (read-stream (make-config) s) "n" "z") + "q and some more")) + T) + +(deftest basic.get-option.2 + (with-input-from-string (s "[n] +p=q +delta=%(gamma)s +z=%(p)s + and some more +") + (equal (get-option (read-stream (make-config) s) "n" "delta" :defaults '(("gamma" . "the gamma value"))) + "the gamma value")) + T) + +(deftest basic.get-option.3 + (with-input-from-string (s "[n] +p=15 +delta=%(gamma)s +z=%(p)s + and some more +") + (equal (get-option (read-stream (make-config) s) "n" "p" :type :number) + 15)) + T) + +(deftest basic.get-option.4 + (with-input-from-string (s "[n] +p=yes +delta=%(gamma)s +z=%(p)s + and some more +") + (equal (get-option (read-stream (make-config) s) "n" "p" :type :boolean) + T)) + T) + +(deftest basic.get-option.5 + (with-input-from-string (s "[n] +p=q +delta=%(gamma)s +z=%(p)s + and some more + +[DEFAULT] +gamma=the gamma value +") + (equal (get-option (read-stream (make-config) s) "n" "delta") + "the gamma value")) + T) + + +(deftest basic.sections + (with-input-from-string (s "[n] post-section header gunk ignored +p=q +z=%(p)s +") + (equal (sections (read-stream (make-config) s)) + '("n"))) + T) + +(deftest basic.comments-only + (typep (with-input-from-string (s "#comments only +") + (read-stream (make-config) s)) 'config) + T) + +(deftest basic.no-newline + (typep (with-input-from-string (s "#comments without trailing \#Newline") + (read-stream (make-config) s)) + 'config) + T) + +(deftest basic.with-defaults + (equal (with-input-from-string (s "[DEFAULT] +def-option = options without trailing newline") + (get-option (read-stream (make-config) s) "DEFAULT" "def-option")) + "options without trailing newline") + T) + +;; newlines only +(deftest basic.newlines-only + (with-input-from-string (s " + + +") + (typep (get-option read-stream (make-config) s) + 'config)) + T) + +;; options +(deftest basic.options + (equal (with-input-from-string (s "[n] +p=q +z=%(p)s +") + (options (read-stream (make-config) s) "n")) '("z" "p")) + T) + +;; items +(deftest basic.items.1 + (equal (with-inputfrom-string (s "[n] +p=q +z=%(p)s +") + (items (read-stream (make-config) s) "n" :expand nil)) '(("z" "%(p)s") ("p" . "q"))) + T) + +(deftest basic.items.2 + (equal (with-input-from-string (s "[n] +p=q +z=%(p)s +") + (items (read-stream (make-config) s) "n" :expand t)) '(("z" . "q") ("p" . "q"))) + T) + +(deftest basic.items.3 + (equal (with-input-from-string (s "[n] +p=q +delta=%(gamma)s +z=%(p)s +") + (items (read-stream (make-config) s) "n" :expand t + :defaults '(("gamma" . "the gamma")))) + '(("z" . "q") ("delta" . "the gamma") ("p" . "q"))) + T) + + +;; sections +(deftest basic.sections.1 + (equal (with-input-from-string (s "[n] +p=q +z=%(p)s + +[v] +[t] +") + (sections (read-stream (make-config) s))) '("t" "v" "n")) + T) + +(deftest basic.sections.2 + (equal (with-input-from-string (s "[n] +p=q +z=%(p)s + +[v] +[t] + +[DEFAULT] +p=t +") + (sections (read-stream (make-config) s))) '("t" "v" "n")) + T) + +;; add-section +(deftest basic.add-section + (with-input-from-string (s "[n] +p=q +z=%(p)s + +[t] + +") + (let ((c (read-stream (make-config) s))) + (unless (has-section-p c "v") + (add-section c "v") + (not (null (has-section-p c "v")))))) + T) + +;; set-option +(deftest basic.set-option.1 + (with-input-from-string (s "[n] +p=q +z=%(p)s + +[t] + +") + (let ((c (read-stream (make-config) s))) + (unless (has-option-p c "t" "b") + (set-option c "t" "b" "ok") + (equal (get-option c "t" "b") "ok")))) + T) + +(deftest basic.set-option.2 + (with-input-from-string (s "[n] +p=q +z=%(p)s + +[t] + +") + (let ((c (read-stream (make-config) s))) + (set-option c "n" "p" "ok") + (equal (get-option c "n" "p") "ok"))) + T) + +;; remove-option +(deftest basic.remove-option + (with-input-from-string (s "[n] +p=q +z=%(p)s + +[t] + +") + (let ((c (read-stream (make-config) s))) + (when (has-option-p c "n" "p") + (remove-option c "n" "p") + (null (has-option-p c "n" "p"))))) + T) + +;; remove-section +(deftest basic.remove-section + (with-input-from-string (s "[n] +p=q +z=%(p)s + +[t] + +") + (let ((c (read-stream (make-config) s))) + (when (has-section-p c "t") + (remove-section c "t") + (null (has-section-p c "t"))))) + T) + + + +;; now the tests that fail +(deftest failures.no-header + (with-input-from-string (s "option-before = section +[header]") + (handler-case + (progn + (read-stream (make-config) s) + nil) + (missing-section-header-error () T))) + T) + +(deftest failures.no-spaced-option-names + (with-input-from-string (s "[n] +option with space = not allowed +") + (handler-case + (progn + (read-stream (make-config) s) + nil) + (parsing-error () T))) + T) + +(deftest failures.recursion + (with-input-from-string (s "[n] +p=%(z)s +z=%(p)s +") + (handler-case + (get-option (read-stream (make-config) s) + "n" ;; section + "p" ;; option + :expand t) + (interpolation-depth-error () T))) + T) + +;; non-erroring non-parsing tests +(deftest miscelaneous + (with-input-from-string (s "[n] +p=%(__name__)s +q=%(z)s +z=hello +") + (let ((p (read-stream (make-config) s))) + (unless (string= (get-option p "n" "p" :expand t) "n") + (error "Unexpected output")) + (unless (string= (get-option p "n" "q" :expand nil) "%(z)s") + (error "Unexpected output")) + (unless (string= (get-option p "n" "q" :expand t) "hello") + (error "Unexpected output")) + (unless (string= (get-option p "n" "z") "hello") + (error "Unexpected output")) + NIL)) + NIL) \ No newline at end of file From ehuelsmann at common-lisp.net Sat Jan 19 19:26:08 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Sat, 19 Jan 2008 14:26:08 -0500 (EST) Subject: [py-configparser-cvs] r12 - trunk Message-ID: <20080119192608.BD6B1240DF@common-lisp.net> Author: ehuelsmann Date: Sat Jan 19 14:26:08 2008 New Revision: 12 Modified: trunk/parser.lisp Log: PROCLAIM -> DECLAIM. Modified: trunk/parser.lisp ============================================================================== --- trunk/parser.lisp (original) +++ trunk/parser.lisp Sat Jan 19 14:26:08 2008 @@ -10,9 +10,9 @@ ;; The reader -(proclaim '(special *line-no* *current-section* *file-name* +(declaim '(special *line-no* *current-section* *file-name* *current-input*)) -(proclaim '(inline %read-char %unread-char)) +(declaim '(inline %read-char %unread-char)) (defun %read-char (stream) (let ((ch (read-char stream nil :eof))) @@ -77,7 +77,7 @@ (make-array 20 :element-type 'cl:character :fill-pointer 0 :adjustable t)) -(proclaim '(inline extend-input)) +(declaim '(inline extend-input)) (defun extend-input (p c) (vector-push-extend c *current-input* 20)) From ehuelsmann at common-lisp.net Sat Jan 19 21:14:37 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Sat, 19 Jan 2008 16:14:37 -0500 (EST) Subject: [py-configparser-cvs] r13 - branches/1.0.x branches/1.0.x/tests trunk Message-ID: <20080119211437.DD6E74C041@common-lisp.net> Author: ehuelsmann Date: Sat Jan 19 16:14:36 2008 New Revision: 13 Added: branches/1.0.x/ - copied from r12, trunk/ Modified: branches/1.0.x/README branches/1.0.x/py-configparser.asd branches/1.0.x/tests/py-configparser-tests.asd trunk/README trunk/py-configparser.asd Log: Branch for 1.0. Modified: branches/1.0.x/README ============================================================================== --- trunk/README (original) +++ branches/1.0.x/README Sat Jan 19 16:14:36 2008 @@ -1,3 +1,52 @@ +$URL$ +$Id$ - +py-configparser +=============== + +This package provides the same functionality as the Python configparser module, +implemented in pure Common Lisp. + + +Differences between the two +=========================== + +The CL version makes a strong distinction in the parser on one hand and the in-memory +storage management on the other hand. Because of it, the CL version doesn't call its +objects 'Parser', but 'config' instead. + +The parser/writer part of the package provides the three functions READ-STREAM, +READ-FILES and WRITE-STREAM, which map from the python variants 'readfp', 'read' +and 'write'. + + +API mapping +=========== + +The functions provided in the Python module (which are all methods of the ConfigParser +class): + +ConfigParser() -> (make-config) +defaults() -> (defaults ) +sections() -> (sections ) +add_section(name) -> (add-section name) +has_section(name) -> (has-section-p name) +options(section_name) -> (options section-name) +has_option(section_name, name) -> (has-option-p section-name name) +read(filenames) -> (read-files filenames) +readfd(fp) -> (read-stream stream) +get(section, option[, raw[, vars]]) -> + (get-option section option &key expand defaults type) +getint(section, option) -> [folded into get-option using 'type' key] +getfloat(section, option) -> [folded into get-option using 'type' key] +getboolean(section, option) -> [folded into get-option using 'type' key] +items(section_name[, raw[, vars]]) -> (items section-name &key expand defaults) +set(section, option, value) -> (set-option section-name option-name value) +write(fp) -> (write-stream stream) +remove_option(section, option) -> (remove-option section-name option-name) +remove_section(section) -> (remove-section section-name) + +Note that the above is just a simple mapping table, but is all you need to get +you started. Documentation from the ConfigParser module should sufficiently document +this package. However minor differences in parameter and method naming may occur. Modified: branches/1.0.x/py-configparser.asd ============================================================================== --- trunk/py-configparser.asd (original) +++ branches/1.0.x/py-configparser.asd Sat Jan 19 16:14:36 2008 @@ -10,7 +10,7 @@ (defsystem py-configparser :name "py-configparser" :author "Erik Huelsmann" - :version "1.0-dev" + :version "1.0.1-dev" :license "MIT" :description "Common Lisp implementation of the Python ConfigParser module" :depends-on (#:parse-number) Modified: branches/1.0.x/tests/py-configparser-tests.asd ============================================================================== --- trunk/tests/py-configparser-tests.asd (original) +++ branches/1.0.x/tests/py-configparser-tests.asd Sat Jan 19 16:14:36 2008 @@ -10,7 +10,7 @@ (defsystem py-configparser-tests :name "py-configparser-tests" :author "Erik Huelsmann" - :version "1.0-dev" + :version "1.0.1-dev" :license "MIT" :description "Tests for 'Common Lisp implementation of the Python ConfigParser module'" :depends-on (#:py-configparser) Modified: trunk/README ============================================================================== --- trunk/README (original) +++ trunk/README Sat Jan 19 16:14:36 2008 @@ -1,3 +1,52 @@ +$URL$ +$Id$ - +py-configparser +=============== + +This package provides the same functionality as the Python configparser module, +implemented in pure Common Lisp. + + +Differences between the two +=========================== + +The CL version makes a strong distinction in the parser on one hand and the in-memory +storage management on the other hand. Because of it, the CL version doesn't call its +objects 'Parser', but 'config' instead. + +The parser/writer part of the package provides the three functions READ-STREAM, +READ-FILES and WRITE-STREAM, which map from the python variants 'readfp', 'read' +and 'write'. + + +API mapping +=========== + +The functions provided in the Python module (which are all methods of the ConfigParser +class): + +ConfigParser() -> (make-config) +defaults() -> (defaults ) +sections() -> (sections ) +add_section(name) -> (add-section name) +has_section(name) -> (has-section-p name) +options(section_name) -> (options section-name) +has_option(section_name, name) -> (has-option-p section-name name) +read(filenames) -> (read-files filenames) +readfd(fp) -> (read-stream stream) +get(section, option[, raw[, vars]]) -> + (get-option section option &key expand defaults type) +getint(section, option) -> [folded into get-option using 'type' key] +getfloat(section, option) -> [folded into get-option using 'type' key] +getboolean(section, option) -> [folded into get-option using 'type' key] +items(section_name[, raw[, vars]]) -> (items section-name &key expand defaults) +set(section, option, value) -> (set-option section-name option-name value) +write(fp) -> (write-stream stream) +remove_option(section, option) -> (remove-option section-name option-name) +remove_section(section) -> (remove-section section-name) + +Note that the above is just a simple mapping table, but is all you need to get +you started. Documentation from the ConfigParser module should sufficiently document +this package. However minor differences in parameter and method naming may occur. Modified: trunk/py-configparser.asd ============================================================================== --- trunk/py-configparser.asd (original) +++ trunk/py-configparser.asd Sat Jan 19 16:14:36 2008 @@ -10,7 +10,7 @@ (defsystem py-configparser :name "py-configparser" :author "Erik Huelsmann" - :version "1.0-dev" + :version "1.1-dev" :license "MIT" :description "Common Lisp implementation of the Python ConfigParser module" :depends-on (#:parse-number) From ehuelsmann at common-lisp.net Sat Jan 19 21:17:41 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Sat, 19 Jan 2008 16:17:41 -0500 (EST) Subject: [py-configparser-cvs] r14 - in tags/1.0.0: . tests Message-ID: <20080119211741.37280240CF@common-lisp.net> Author: ehuelsmann Date: Sat Jan 19 16:17:40 2008 New Revision: 14 Added: tags/1.0.0/ - copied from r13, branches/1.0.x/ Modified: tags/1.0.0/py-configparser.asd tags/1.0.0/tests/py-configparser-tests.asd Log: Tag 1.0.0. Modified: tags/1.0.0/py-configparser.asd ============================================================================== --- branches/1.0.x/py-configparser.asd (original) +++ tags/1.0.0/py-configparser.asd Sat Jan 19 16:17:40 2008 @@ -10,7 +10,7 @@ (defsystem py-configparser :name "py-configparser" :author "Erik Huelsmann" - :version "1.0.1-dev" + :version "1.0.0" :license "MIT" :description "Common Lisp implementation of the Python ConfigParser module" :depends-on (#:parse-number) Modified: tags/1.0.0/tests/py-configparser-tests.asd ============================================================================== --- branches/1.0.x/tests/py-configparser-tests.asd (original) +++ tags/1.0.0/tests/py-configparser-tests.asd Sat Jan 19 16:17:40 2008 @@ -10,7 +10,7 @@ (defsystem py-configparser-tests :name "py-configparser-tests" :author "Erik Huelsmann" - :version "1.0.1-dev" + :version "1.0.0" :license "MIT" :description "Tests for 'Common Lisp implementation of the Python ConfigParser module'" :depends-on (#:py-configparser) From ehuelsmann at common-lisp.net Sat Jan 19 21:25:23 2008 From: ehuelsmann at common-lisp.net (ehuelsmann at common-lisp.net) Date: Sat, 19 Jan 2008 16:25:23 -0500 (EST) Subject: [py-configparser-cvs] r15 - in public_html: . releases Message-ID: <20080119212523.199DB4B023@common-lisp.net> Author: ehuelsmann Date: Sat Jan 19 16:25:23 2008 New Revision: 15 Added: public_html/releases/ public_html/releases/py-configparser-1.0.tar.gz (contents, props changed) Modified: public_html/index.shtml Log: Add 1.0 release. Modified: public_html/index.shtml ============================================================================== --- public_html/index.shtml (original) +++ public_html/index.shtml Sat Jan 19 16:25:23 2008 @@ -1,63 +1,60 @@ - - - - - <!--#include virtual="project-name" --> - - - - - -
-

-
- -

Project description

-

The py-configparser package implements the - - ConfigParser Python module functionality in Common Lisp.

- -

In short, it implements reading and writing of .INI-file style - configuration files with sections containing key/value pairs of - configuration options. In line with the functionalities in the python - module, does this package implement basic interpolation of option values - in other options. -

- -

The main reason for starting the project is to be able to share - configuration files between Python and Common Lisp. Other projects - may be using the python/INI file style files too and the project is - by no means limited to the ConfigParser functionality. So, if you - have any wishes and/or nice extentions based on this format, please - send your contributions or extention requests.

- -

Examples:

-
-
-[my-section]
-option1 = value1
-option2: value2
-
-[interpolated-section]
-option1 = value
-option2 = value2 and %(option1)s
-# this is a comment; the above line evaluates to "value2 and value"
-
-# empty lines and comments are skipped
- 
- - -

Releases

-

Having recently started (Christmas hacking, 2007), the project doesn't -have any official releases yet. The code however should be useable and -the only reason there's no release is because there are no unit tests yet. -(Yea, working on it....)

- -
- - - - + + + + + <!--#include virtual="project-name" --> + + + + + +
+

+
+ +

Project description

+

The py-configparser package implements the + + ConfigParser Python module functionality in Common Lisp.

+ +

In short, it implements reading and writing of .INI-file style + configuration files with sections containing key/value pairs of + configuration options. In line with the functionalities in the python + module, does this package implement basic interpolation of option values + in other options. +

+ +

The main reason for starting the project is to be able to share + configuration files between Python and Common Lisp. Other projects + may be using the python/INI file style files too and the project is + by no means limited to the ConfigParser functionality. So, if you + have any wishes and/or nice extentions based on this format, please + send your contributions or extention requests.

+ +

Examples:

+
+
+[my-section]
+option1 = value1
+option2: value2
+
+[interpolated-section]
+option1 = value
+option2 = value2 and %(option1)s
+# this is a comment; the above line evaluates to "value2 and value"
+
+# empty lines and comments are skipped
+ 
+ + +

Releases

+

The 1.0 release can be downloaded from http://common-lisp.net/project/py-configparser/releases/py-configparser-1.0.tar.gz

+ +
+ + + + Added: public_html/releases/py-configparser-1.0.tar.gz ============================================================================== Binary file. No diff available.