Skip to content

Commit 60b8fe6

Browse files
committed
Made internal mapper private.
1 parent 7c48568 commit 60b8fe6

File tree

1 file changed

+235
-0
lines changed

1 file changed

+235
-0
lines changed

valve_keyvalues_python/keyvalues.py

+235
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
__author__ = "Jiri Novotny"
2+
__version__ = "1.0.0"
3+
4+
class KeyValues(dict):
5+
"""
6+
Class for manipulation with Valve KeyValue (KV) files (VDF format). Parses the KV file to object with dict interface.
7+
Allows to write objects with dict interface to KV files.
8+
"""
9+
10+
__re = __import__('re')
11+
__sys = __import__('sys')
12+
__OrderedDict = __import__('collections').OrderedDict
13+
__regexs = {
14+
"key": __re.compile(r"""(['"])(?P<key>((?!\1).)*)\1(?!.)""", __re.I),
15+
"key_value": __re.compile(r"""(['"])(?P<key>((?!\1).)*)\1(\s+|)['"](?P<value>((?!\1).)*)\1""", __re.I)
16+
}
17+
18+
def __init__(self, mapper=None, filename=None, encoding="utf-8", mapper_type=__OrderedDict, key_modifier=None, key_sorter=None):
19+
"""
20+
:param mapper: initialize with own dict-like mapper
21+
:param filename: filename of KV file, which will be parsed to dict structure. Mapper param must not be specified when using this param!
22+
:param encoding: KV file encoding. Default: 'utf-8'
23+
:param mapper_type: which mapper will be used for storing KV. It must have the dict interface, i.e. allow to do the 'mapper[key] = value action'.
24+
default: 'collections.OrderedDict'
25+
For example you can use the 'dict' type.
26+
:param key_modifier: function for modifying the keys, e.g. the function 'string.lower' will make all the keys lower
27+
:param key_sorter: function for sorting the keys when dumping/writing/str, e.g. using the function 'sorted' will show KV keys in alphabetical order
28+
"""
29+
30+
self.__sys.setrecursionlimit(100000)
31+
self.mapper_type = type(mapper) if mapper else mapper_type
32+
self.key_modifier = key_modifier
33+
self.key_sorter = key_sorter
34+
35+
if not mapper and not filename:
36+
self.__mapper = mapper_type()
37+
return
38+
39+
if mapper:
40+
self.__mapper = mapper
41+
return
42+
43+
if type(filename) == str:
44+
self.parse(filename)
45+
else:
46+
raise Exception("'filename' argument must be string!")
47+
48+
def __setitem__(self, key, item):
49+
self.__mapper[key] = item
50+
51+
def __getitem__(self, key):
52+
return self.__mapper[key]
53+
54+
def __repr__(self):
55+
#return repr(self.__mapper)
56+
return self.dump(self.__mapper)
57+
58+
def __len__(self):
59+
return len(self.__mapper)
60+
61+
def __delitem__(self, key):
62+
del self.__mapper[key]
63+
64+
def clear(self):
65+
return self.__mapper.clear()
66+
67+
def copy(self):
68+
"""
69+
:return: mapper of KeyValues
70+
"""
71+
return self.__mapper.copy()
72+
73+
def has_key(self, k):
74+
return self.__mapper.has_key(k)
75+
76+
def pop(self, k, d=None):
77+
return self.__mapper.pop(k, d)
78+
79+
def update(self, *args, **kwargs):
80+
return self.__mapper.update(*args, **kwargs)
81+
82+
def keys(self):
83+
return self.__mapper.keys()
84+
85+
def values(self):
86+
return self.__mapper.values()
87+
88+
def items(self):
89+
return self.__mapper.items()
90+
91+
def pop(self, *args):
92+
return self.__mapper.pop(*args)
93+
94+
def __cmp__(self, dict):
95+
return cmp(self.__mapper, dict)
96+
97+
def __contains__(self, item):
98+
return item in self.__mapper
99+
100+
def __iter__(self):
101+
return iter(self.__mapper)
102+
103+
def __unicode__(self):
104+
return unicode(repr(self.__mapper))
105+
106+
def __str__(self):
107+
return self.dump()
108+
109+
def __key_modifier(self, key, key_modifier):
110+
"""
111+
Modifies the key string using the 'key_modifier' function.
112+
113+
:param key:
114+
:param key_modifier:
115+
:return:
116+
"""
117+
118+
key_modifier = key_modifier or self.key_modifier
119+
120+
if key_modifier:
121+
return key_modifier(key)
122+
else:
123+
return key
124+
125+
def __parse(self, lines, mapper_type, i=0, key_modifier=None):
126+
"""
127+
Recursively maps the KeyValues from list of file lines.
128+
129+
:param lines:
130+
:param mapper_type:
131+
:param i:
132+
:param key_modifier:
133+
:return:
134+
"""
135+
136+
key = False
137+
_mapper = mapper_type()
138+
139+
try:
140+
while i < len(lines):
141+
if lines[i].startswith("{"):
142+
if not key:
143+
raise Exception("'{{' found without key at line {}".format(i + 1))
144+
_mapper[key], i = self.__parse(lines, i=i+1, mapper_type=mapper_type, key_modifier=key_modifier)
145+
continue
146+
elif lines[i].startswith("}"):
147+
return _mapper, i + 1
148+
elif self.__re.match(self.__regexs["key"], lines[i]):
149+
key = self.__key_modifier(self.__re.search(self.__regexs["key"], lines[i]).group("key"), key_modifier)
150+
i += 1
151+
continue
152+
elif self.__re.match(self.__regexs["key_value"], lines[i]):
153+
groups = self.__re.search(self.__regexs["key_value"], lines[i])
154+
_mapper[self.__key_modifier(groups.group("key"), key_modifier)] = groups.group("value")
155+
i += 1
156+
elif self.__re.match(self.__regexs["key_value"], lines[i] + lines[i+1]):
157+
groups = self.__re.search(self.__regexs["key_value"], lines[i] + " " + lines[i+1])
158+
_mapper[self.__key_modifier(groups.group("key"), key_modifier)] = groups.group("value")
159+
i += 1
160+
else:
161+
i += 1
162+
except IndexError:
163+
pass
164+
165+
return _mapper
166+
167+
def parse(self, filename, encoding="utf-8", mapper_type=__OrderedDict, key_modifier=None):
168+
"""
169+
Parses the KV file so this instance can be accessed by dict interface.
170+
171+
:param filename: name of KV file
172+
:param encoding: KV file encoding. Default: 'utf-8'
173+
:param mapper_type: which mapper will be used for storing KV. It must have the dict interface, i.e. allow to do the 'mapper[key] = value action'.
174+
default: 'collections.OrderedDict'
175+
For example you can use the 'dict' type.
176+
This will override the instance's 'mapper_type' if specified during instantiation.
177+
:param key_modifier: function for modifying the keys, e.g. the function 'string.lower' will make all the keys lower.
178+
This will override the instance's 'key_modifier' if specified during instantiation.
179+
"""
180+
181+
with open(filename, mode="r", encoding=encoding) as f:
182+
self.__mapper = self.__parse([line.strip() for line in f.readlines()],
183+
mapper_type=mapper_type or self.mapper_type,
184+
key_modifier=key_modifier or self.key_modifier)
185+
186+
def __tab(self, string, level, quotes=False):
187+
if quotes:
188+
return '{}"{}"'.format(level * "\t", string)
189+
else:
190+
return '{}{}'.format(level * "\t", string)
191+
192+
def __dump(self, mapper, key_sorter=None, level=0):
193+
string = ""
194+
195+
if key_sorter:
196+
keys = key_sorter(mapper.keys())
197+
else:
198+
keys = mapper.keys()
199+
200+
for key in keys:
201+
string += self.__tab(key, level, quotes=True)
202+
if type(mapper[key]) == str:
203+
string += '\t "{}"\n'.format(mapper[key])
204+
else:
205+
string += "\n" + self.__tab("{\n", level)
206+
string += self.__dump(mapper[key], key_sorter=key_sorter, level=level+1)
207+
string += self.__tab("}\n", level)
208+
209+
return string
210+
211+
def dump(self, mapper=None, key_sorter=None):
212+
"""
213+
Dumps the KeyValues mapper to string.
214+
215+
:param mapper: you can dump your own object with dict interface
216+
:param key_sorter: function for sorting the keys when dumping/writing/str, e.g. using the function 'sorted' will show KV in alphabetical order.
217+
This will override the instance's 'key_sorter' if specified during instantiation.
218+
:return: string
219+
"""
220+
221+
return self.__dump(mapper=mapper or self.__mapper, key_sorter=key_sorter or self.key_sorter)
222+
223+
def write(self, filename, encoding="utf-8", mapper=None, key_sorter=None):
224+
"""
225+
Writes the KeyValues to file.
226+
227+
:param filename: output KV file name
228+
:param encoding: output KV file encoding. Default: 'utf-8'
229+
:param mapper: you can write your own object with dict interface
230+
:param key_sorter: key_sorter: function for sorting the keys when dumping/writing/str, e.g. using the function 'sorted' will show KV in alphabetical order.
231+
This will override the instance's 'key_sorter' if specified during instantiation.
232+
"""
233+
234+
with open(filename, mode="w", encoding=encoding) as f:
235+
f.write(self.dump(mapper=mapper or self.__mapper, key_sorter=key_sorter or self.key_sorter))

0 commit comments

Comments
 (0)