pythonconfig 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +7 -0
- data/Manifest.txt +7 -0
- data/README.txt +95 -0
- data/Rakefile +12 -0
- data/bin/pythonconfig +3 -0
- data/lib/pythonconfig.rb +156 -0
- data/test/test_pythonconfig.rb +82 -0
- metadata +72 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
= pythonconfig
|
2
|
+
|
3
|
+
* http://www.carboni.ca/
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Class for parsing and writing Python configuration files created by the
|
8
|
+
ConfigParser classes in Python. These files are structured like this:
|
9
|
+
[Section Name]
|
10
|
+
key = value
|
11
|
+
otherkey: othervalue
|
12
|
+
|
13
|
+
[Other Section]
|
14
|
+
key: value3
|
15
|
+
otherkey = value4
|
16
|
+
|
17
|
+
Leading whitespace before values are trimmed, and the key must be the at the
|
18
|
+
start of the line - no leading whitespace there. You can use : or = .
|
19
|
+
|
20
|
+
Multiline values are supported, as long as the second (or third, etc.) lines
|
21
|
+
start with whitespace:
|
22
|
+
|
23
|
+
[Section]
|
24
|
+
bigstring: This is a very long string, so I'm not sure I'll be
|
25
|
+
able to fit it on one line, but as long as
|
26
|
+
there is one space before each line, I'm ok. Tabs work too.
|
27
|
+
|
28
|
+
Also, this class supports interpolation:
|
29
|
+
[Awards]
|
30
|
+
output: Congratulations for winning %(prize)!
|
31
|
+
prize: the lottery
|
32
|
+
Will result in:
|
33
|
+
config.sections["Awards"]["output"] == "Congratulations for winning the lottery!"
|
34
|
+
|
35
|
+
You can also access the sections with the dot operator, but only with all-lowercase:
|
36
|
+
[Awards]
|
37
|
+
key:value
|
38
|
+
[prizes]
|
39
|
+
lottery=3.2 million
|
40
|
+
|
41
|
+
config.awards["key"] #=> "value"
|
42
|
+
config.prizes["lottery"] #=> "3.2 million"
|
43
|
+
|
44
|
+
You can modify any values you want, though to add sections, you should use the add_section
|
45
|
+
method.
|
46
|
+
config.sections["prizes"]["lottery"] = "100 dollars" # someone hit the jackpot
|
47
|
+
config.add_section("Candies")
|
48
|
+
config.candies["green"] = "tasty"
|
49
|
+
When you want to output a configuration, just call its +to_s+ method.
|
50
|
+
File.open("output.ini","w") do |out|
|
51
|
+
out.write config.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
== FEATURES/PROBLEMS:
|
55
|
+
|
56
|
+
* Multiple sections are parsed
|
57
|
+
* Interpolation using %(key) supported
|
58
|
+
* Multiline values supported
|
59
|
+
|
60
|
+
== SYNOPSIS:
|
61
|
+
|
62
|
+
See the Description.
|
63
|
+
|
64
|
+
== REQUIREMENTS:
|
65
|
+
|
66
|
+
* none
|
67
|
+
|
68
|
+
== INSTALL:
|
69
|
+
|
70
|
+
* sudo gem install pythonconfig
|
71
|
+
|
72
|
+
== LICENSE:
|
73
|
+
|
74
|
+
(The MIT License)
|
75
|
+
|
76
|
+
Copyright (c) 2009 FIX
|
77
|
+
|
78
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
79
|
+
a copy of this software and associated documentation files (the
|
80
|
+
'Software'), to deal in the Software without restriction, including
|
81
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
82
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
83
|
+
permit persons to whom the Software is furnished to do so, subject to
|
84
|
+
the following conditions:
|
85
|
+
|
86
|
+
The above copyright notice and this permission notice shall be
|
87
|
+
included in all copies or substantial portions of the Software.
|
88
|
+
|
89
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
90
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
91
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
92
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
93
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
94
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
95
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/pythonconfig.rb'
|
6
|
+
|
7
|
+
Hoe.new('pythonconfig', PythonConfig::VERSION) do |p|
|
8
|
+
# p.rubyforge_name = 'pythonconfigx' # if different than lowercase project name
|
9
|
+
p.developer('FIX', 'FIX@example.com')
|
10
|
+
end
|
11
|
+
|
12
|
+
# vim: syntax=Ruby
|
data/bin/pythonconfig
ADDED
data/lib/pythonconfig.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
# = PythonConfig
|
3
|
+
# Class for parsing and writing Python configuration files created by the
|
4
|
+
# ConfigParser classes in Python. These files are structured like this:
|
5
|
+
# [Section Name]
|
6
|
+
# key = value
|
7
|
+
# otherkey: othervalue
|
8
|
+
#
|
9
|
+
# [Other Section]
|
10
|
+
# key: value3
|
11
|
+
# otherkey = value4
|
12
|
+
#
|
13
|
+
# Leading whitespace before values are trimmed, and the key must be the at the
|
14
|
+
# start of the line - no leading whitespace there. You can use : or = .
|
15
|
+
#
|
16
|
+
# Multiline values are supported, as long as the second (or third, etc.) lines
|
17
|
+
# start with whitespace:
|
18
|
+
#
|
19
|
+
# [Section]
|
20
|
+
# bigstring: This is a very long string, so I'm not sure I'll be
|
21
|
+
# able to fit it on one line, but as long as
|
22
|
+
# there is one space before each line, I'm ok. Tabs work too.
|
23
|
+
#
|
24
|
+
# Also, this class supports interpolation:
|
25
|
+
# [Awards]
|
26
|
+
# output: Congratulations for winning %(prize)!
|
27
|
+
# prize: the lottery
|
28
|
+
# Will result in:
|
29
|
+
# config.sections["Awards"]["output"] == "Congratulations for winning the lottery!"
|
30
|
+
#
|
31
|
+
# You can also access the sections with the dot operator, but only with all-lowercase:
|
32
|
+
# [Awards]
|
33
|
+
# key:value
|
34
|
+
# [prizes]
|
35
|
+
# lottery=3.2 million
|
36
|
+
#
|
37
|
+
# config.awards["key"] #=> "value"
|
38
|
+
# config.prizes["lottery"] #=> "3.2 million"
|
39
|
+
#
|
40
|
+
# You can modify any values you want, though to add sections, you should use the add_section
|
41
|
+
# method.
|
42
|
+
# config.sections["prizes"]["lottery"] = "100 dollars" # someone hit the jackpot
|
43
|
+
# config.add_section("Candies")
|
44
|
+
# config.candies["green"] = "tasty"
|
45
|
+
# When you want to output a configuration, just call its +to_s+ method.
|
46
|
+
# File.open("output.ini","w") do |out|
|
47
|
+
# out.write config.to_s
|
48
|
+
# end
|
49
|
+
module PythonConfig
|
50
|
+
VERSION = '1.0.0'
|
51
|
+
MAX_INTERPOLATION_DEPTH = 200
|
52
|
+
# Don't make recursive interpolating values!
|
53
|
+
class InterpolationTooDeepError < StandardError; end
|
54
|
+
# This is the main class that handles configurations. You parse, modify, and output
|
55
|
+
# through this class. See the README for tons of examples.
|
56
|
+
class ConfigParser
|
57
|
+
attr_reader :sections
|
58
|
+
|
59
|
+
SECTION_REGEXP = /\[([^\[\]]*)\]/
|
60
|
+
ASSIGNMENT_REGEXP = /([^:=\s]+)\s*[:=]\s*([^\n]*?)$/
|
61
|
+
LONG_HEADER_REGEXP = /^([ \t]+)([^\n]+)$/
|
62
|
+
# Creates a new ConfigParser. If +io+ is provided, the configuraiton file is read
|
63
|
+
# from the io.
|
64
|
+
def initialize(io = nil)
|
65
|
+
@sections = {}
|
66
|
+
io.each do |line|
|
67
|
+
parse_line line
|
68
|
+
end unless io.nil?
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_line line #:nodoc:
|
72
|
+
|
73
|
+
if line =~ SECTION_REGEXP
|
74
|
+
section_name = $1
|
75
|
+
@cursection = add_section section_name
|
76
|
+
elsif line =~ ASSIGNMENT_REGEXP
|
77
|
+
@cursection[$1] = $2
|
78
|
+
@cur_assignment = $1
|
79
|
+
elsif line =~ LONG_HEADER_REGEXP
|
80
|
+
@cursection[@cur_assignment] += " " + $2
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns the names of all the sections, which can be used for keys into the sections
|
85
|
+
def section_names
|
86
|
+
@sections.keys
|
87
|
+
end
|
88
|
+
|
89
|
+
# Creates a new section, with the values as provided by the (optional) values parameter
|
90
|
+
def add_section(section_name, values={})
|
91
|
+
newsection = ConfigSection.new(values)
|
92
|
+
@sections[section_name] = newsection
|
93
|
+
self.instance_eval %Q{
|
94
|
+
def #{section_name.downcase}
|
95
|
+
@sections["#{section_name}"]
|
96
|
+
end
|
97
|
+
}
|
98
|
+
newsection
|
99
|
+
end
|
100
|
+
|
101
|
+
# Returns the section given by +section+
|
102
|
+
def [](section)
|
103
|
+
@sections[section]
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns the configuration as a string that can be output to a file. Does not perform
|
107
|
+
# interpolation before writing.
|
108
|
+
def to_s
|
109
|
+
output = ""
|
110
|
+
@sections.each do |k,v|
|
111
|
+
output << "[#{k}]\n"
|
112
|
+
output << v.to_s
|
113
|
+
end
|
114
|
+
output
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
# = ConfigSection
|
119
|
+
# This is a simple section in a config document - treat it exactly like a hash,
|
120
|
+
# whose keys are the keys in the config, and whose value are the values in the config.
|
121
|
+
#
|
122
|
+
# This is a separate class entirely because it has to handle the magical interpolation
|
123
|
+
# that allows this ini file:
|
124
|
+
# [Awards]
|
125
|
+
# output: Congratulations for winning %(prize)!
|
126
|
+
# prize: the lottery
|
127
|
+
# To result in:
|
128
|
+
# config.sections["Awards"]["output"] == "Congratulations for winning the lottery!"
|
129
|
+
#
|
130
|
+
class ConfigSection < DelegateClass(Hash)
|
131
|
+
def initialize(source)
|
132
|
+
@source_hash = source
|
133
|
+
super(@source_hash)
|
134
|
+
end
|
135
|
+
def [](key) #:nodoc:
|
136
|
+
str = @source_hash[key]
|
137
|
+
interpolate(str)
|
138
|
+
end
|
139
|
+
|
140
|
+
def interpolate(str, cur_depth=0) #:nodoc:
|
141
|
+
raise InterpolationTooDeepError.new("Interpolation too deep!") if cur_depth > PythonConfig::MAX_INTERPOLATION_DEPTH
|
142
|
+
nextval = str
|
143
|
+
nextval = str.gsub(/\%\((.*)\)/,@source_hash[$1]) if str =~ /%\((.*)\)/
|
144
|
+
nextval = interpolate(nextval,cur_depth+1) if nextval =~ /%\((.*)\)/
|
145
|
+
nextval
|
146
|
+
end
|
147
|
+
|
148
|
+
def to_s #:nodoc:
|
149
|
+
output = ""
|
150
|
+
@source_hash.each do |k,v|
|
151
|
+
output << "#{k} = #{v.gsub(/\n/,"\n ")}" << "\n"
|
152
|
+
end
|
153
|
+
output
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "pythonconfig"
|
3
|
+
|
4
|
+
class TestConfigParser < Test::Unit::TestCase
|
5
|
+
include PythonConfig
|
6
|
+
def test_sections
|
7
|
+
cfg = nil
|
8
|
+
File.open(File.join(File.dirname(__FILE__),"inputfile.ini"), "r") do |inp|
|
9
|
+
cfg = ConfigParser.new(inp)
|
10
|
+
end
|
11
|
+
assert_equal(["cool_section", "evil", "other_stuff"],cfg.sections.keys.sort)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_basic_assignments
|
15
|
+
cfg = nil
|
16
|
+
File.open(File.join(File.dirname(__FILE__),"inputfile.ini"), "r") do |inp|
|
17
|
+
cfg = ConfigParser.new(inp)
|
18
|
+
end
|
19
|
+
assert_equal("true",cfg.sections["cool_section"]["lame"])
|
20
|
+
assert_equal("8.7",cfg.sections["other_stuff"]["sweetness"])
|
21
|
+
assert_equal("false",cfg.sections["cool_section"]["cool"])
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_multiline
|
25
|
+
cfg = nil
|
26
|
+
File.open(File.join(File.dirname(__FILE__),"inputfile.ini"), "r") do |inp|
|
27
|
+
cfg = ConfigParser.new(inp)
|
28
|
+
end
|
29
|
+
assert_equal("super good stuff that i bet you didn't think was possible did you now?", cfg.sections["cool_section"]["multiline"])
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_dot_accessor
|
33
|
+
cfg = nil
|
34
|
+
File.open(File.join(File.dirname(__FILE__),"inputfile.ini"), "r") do |inp|
|
35
|
+
cfg = ConfigParser.new(inp)
|
36
|
+
end
|
37
|
+
assert_equal(cfg.sections["cool_section"], cfg.cool_section)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_array_accessor
|
41
|
+
cfg = nil
|
42
|
+
File.open(File.join(File.dirname(__FILE__),"inputfile.ini"), "r") do |inp|
|
43
|
+
cfg = ConfigParser.new(inp)
|
44
|
+
end
|
45
|
+
assert_equal(cfg.sections["cool_section"], cfg["cool_section"])
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_allow_empty
|
49
|
+
cfg = nil
|
50
|
+
File.open(File.join(File.dirname(__FILE__),"inputfile.ini"), "r") do |inp|
|
51
|
+
cfg = ConfigParser.new(inp)
|
52
|
+
end
|
53
|
+
assert_equal("", cfg.sections["cool_section"]["empty"])
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_interpolation
|
57
|
+
cfg = nil
|
58
|
+
File.open(File.join(File.dirname(__FILE__),"inputfile.ini"), "r") do |inp|
|
59
|
+
cfg = ConfigParser.new(inp)
|
60
|
+
end
|
61
|
+
assert_equal("your sweetness is 8.7!", cfg.sections["other_stuff"]["output"])
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_interpolation_recursion_exception
|
65
|
+
cfg = nil
|
66
|
+
File.open(File.join(File.dirname(__FILE__),"inputfile.ini"), "r") do |inp|
|
67
|
+
cfg = ConfigParser.new(inp)
|
68
|
+
end
|
69
|
+
assert_raises InterpolationTooDeepError do
|
70
|
+
cfg.sections["evil"]["firstone"]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_add_section
|
75
|
+
cfg = nil
|
76
|
+
File.open(File.join(File.dirname(__FILE__),"inputfile.ini"), "r") do |inp|
|
77
|
+
cfg = ConfigParser.new(inp)
|
78
|
+
end
|
79
|
+
cfg.add_section "new_section"
|
80
|
+
assert_equal(["cool_section", "evil","new_section", "other_stuff"],cfg.sections.keys.sort)
|
81
|
+
end
|
82
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pythonconfig
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- FIX
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-03-28 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.11.0
|
24
|
+
version:
|
25
|
+
description: "Class for parsing and writing Python configuration files created by the ConfigParser classes in Python. These files are structured like this: [Section Name] key = value otherkey: othervalue [Other Section] key: value3 otherkey = value4 Leading whitespace before values are trimmed, and the key must be the at the start of the line - no leading whitespace there. You can use : or = . Multiline values are supported, as long as the second (or third, etc.) lines start with whitespace: [Section] bigstring: This is a very long string, so I'm not sure I'll be able to fit it on one line, but as long as there is one space before each line, I'm ok. Tabs work too. Also, this class supports interpolation: [Awards] output: Congratulations for winning %(prize)! prize: the lottery Will result in: config.sections[\"Awards\"][\"output\"] == \"Congratulations for winning the lottery!\" You can also access the sections with the dot operator, but only with all-lowercase: [Awards] key:value [prizes] lottery=3.2 million config.awards[\"key\"] #=> \"value\" config.prizes[\"lottery\"] #=> \"3.2 million\" You can modify any values you want, though to add sections, you should use the add_section method. config.sections[\"prizes\"][\"lottery\"] = \"100 dollars\" # someone hit the jackpot config.add_section(\"Candies\") config.candies[\"green\"] = \"tasty\" When you want to output a configuration, just call its +to_s+ method. File.open(\"output.ini\",\"w\") do |out| out.write config.to_s end"
|
26
|
+
email:
|
27
|
+
- FIX@example.com
|
28
|
+
executables:
|
29
|
+
- pythonconfig
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files:
|
33
|
+
- History.txt
|
34
|
+
- Manifest.txt
|
35
|
+
- README.txt
|
36
|
+
files:
|
37
|
+
- History.txt
|
38
|
+
- Manifest.txt
|
39
|
+
- README.txt
|
40
|
+
- Rakefile
|
41
|
+
- bin/pythonconfig
|
42
|
+
- lib/pythonconfig.rb
|
43
|
+
- test/test_pythonconfig.rb
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: http://www.carboni.ca/
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options:
|
48
|
+
- --main
|
49
|
+
- README.txt
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project: pythonconfig
|
67
|
+
rubygems_version: 1.3.1
|
68
|
+
signing_key:
|
69
|
+
specification_version: 2
|
70
|
+
summary: Class for parsing and writing Python configuration files created by the ConfigParser classes in Python
|
71
|
+
test_files:
|
72
|
+
- test/test_pythonconfig.rb
|