TableRowData: new methods __copy__() and format()
[galtack.git] / galcon / net_common.py
bloba309a35dec489771892392b72b57455f37773a84
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 """
4 Galcon networking - code common to all the Galcon networking.
6 Currently this consists of data structures.
7 """
8 # Copyright (C) 2007 Felix Rabe <public@felixrabe.textdriven.com>
9 # Copyright (C) 2007 Michael Carter
11 # Permission is hereby granted, free of charge, to any person obtaining a
12 # copy of this software and associated documentation files (the
13 # "Software"), to deal in the Software without restriction, including
14 # without limitation the rights to use, copy, modify, merge, publish,
15 # distribute, sublicense, and/or sell copies of the Software, and to permit
16 # persons to whom the Software is furnished to do so, subject to the
17 # following conditions:
19 # The above copyright notice and this permission notice shall be included
20 # in all copies or substantial portions of the Software.
22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
26 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
27 # OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
28 # THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 # Recommended line length or text width: 75 characters.
32 import copy
35 class TableRowData(object):
36 """
37 Base class for rows of tables.
39 Actually, it is very easy to use. Take the *Info classes further down
40 this file as a starting point.
41 """
43 # COL_SPEC example to document the structure - override in derived
44 # classes:
45 # (You can use TableRowData.build_col_spec_from_string() to build
46 # COL_SPEC and COL_SPEC_DICT automatically.)
47 COL_SPEC = [
49 "name": "user_name",
50 "type": str,
51 "caption": "User",
52 "visible": True,
53 "default": "No Name (default, optional)",
56 "name": "user_password",
57 "type": str,
58 "caption": "Password",
59 "visible": False,
60 "default": "", # unset
63 "name": "logged_in",
64 "type": bool,
65 "caption": "Logged In?",
66 "visible": False,
67 "default": False,
69 # ...
72 # COL_SPEC_DICT example to document the structure - override in derived
73 # classes:
74 # (You can use TableRowData.build_col_spec_dict() to build
75 # COL_SPEC_DICT automatically.)
76 COL_SPEC_DICT = {
77 "user_name": COL_SPEC[0],
78 "user_password": COL_SPEC[1],
79 "logged_in": COL_SPEC[2],
80 # ...
83 def __init__(self, *args, **kwargs):
84 """
85 Initialize in a user-friendly way.
87 Examples:
88 TableRowData("Peter").user_name == "Peter"
89 TableRowData(user_name = "Peter")["user_name"] == "Peter"
90 """
91 self.__data = {}
92 for i, arg in enumerate(args):
93 spec = self.COL_SPEC[i]
94 self.__data[spec["name"]] = spec["type"](arg)
95 for spec in self.COL_SPEC:
96 n = spec["name"]
97 t = spec["type"]
98 if n in kwargs:
99 self.__data[n] = t(kwargs.pop(n))
100 elif n not in self.__data:
101 self.__data[n] = t(spec["default"])
102 super(TableRowData, self).__init__(self, **kwargs)
104 def __copy__(self):
106 Implement copy.copy(TableRowData_instance) properly.
108 row_data = self.__class__()
109 for key, value in self.__data.iteritems():
110 row_data[key] = copy.copy(value)
111 return row_data
113 @staticmethod
114 def build_col_spec_from_string(string):
116 Build COL_SPEC and COL_SPEC_DICT by parsing a string.
118 Return: (COL_SPEC, COL_SPEC_DICT)
120 Example:
121 TableRowData.build_col_spec_from_string(
123 user_name; str; User; No Name (default, optional)
124 user_password; str; (Password)
125 logged_in; bool; (Logged In?); False
127 ) == (TableRowData.COL_SPEC, TableRowData.COL_SPEC_DICT)
129 lines = string.strip().split("\n")
130 lines = map(lambda line:
131 map( lambda i: i.strip(), line.split(";") ),
132 lines)
133 col_spec = []
134 for line in lines:
135 # "name"
136 name = line[0]
137 # "type"
138 type = __builtins__[line[1]]
139 # "caption"
140 caption = line[2]
141 # "visible"
142 visible = True
143 if caption[0] == "(" and caption[-1] == ")":
144 caption = caption[1:-1]
145 visible = False
146 # "default"
147 default = type()
148 if len(line) > 3:
149 if type == str:
150 default = line[-1]
151 else:
152 default = type(eval(line[-1]))
153 spec_dict = {
154 "name": name,
155 "type": type,
156 "caption": caption,
157 "visible": visible,
158 "default": default,
160 col_spec.append(spec_dict)
161 return (col_spec, TableRowData.build_col_spec_dict(col_spec))
163 @staticmethod
164 def build_col_spec_dict(col_spec):
166 Build COL_SPEC_DICT from COL_SPEC.
168 Return: COL_SPEC_DICT
170 Example:
171 TableRowData.build_col_spec_dict(
172 TableRowData.COL_SPEC
173 ) == TableRowData.COL_SPEC_DICT
175 col_spec_dict = {}
176 for spec in col_spec:
177 col_spec_dict[spec["name"]] = spec
178 return col_spec_dict
180 def get_data_tuple(self):
182 Get a tuple containing all the data.
184 tup = []
185 for spec in self.COL_SPEC:
186 tup.append(self.__data[spec["name"]])
187 return tuple(tup)
189 def get_visible_data_tuple(self):
191 Get a tuple containing all the visible data.
193 tup = []
194 for i, x in enumerate(self.get_tuple()):
195 if self.COL_SPEC[i]["visible"] == True:
196 tup.append(x)
197 return tuple(tup)
199 @classmethod
200 def get_col_types(cls):
202 Get a tuple containing all the data types.
204 return tuple(s["type"] for s in cls.COL_SPEC)
206 @classmethod
207 def get_visible_col_types(cls):
209 Get a tuple containing all the visible data types.
211 tup = []
212 for i, x in enumerate(cls.get_types()):
213 if cls.COL_SPEC[i]["visible"] == True:
214 tup.append(x)
215 return tuple(tup)
217 def __getitem__(self, name):
219 Make self["user_name"] and self[0] work.
221 i = None
222 try:
223 i = int(name)
224 except: pass
225 if i is not None:
226 spec = self.COL_SPEC[i]
227 return self.__data[spec["name"]]
228 try:
229 return self.__data[name]
230 except:
231 raise AttributeError, name
233 def __getattr__(self, name):
235 Make self.user_name work.
237 return self.__getitem__(name)
239 def __setitem__(self, name, value):
241 Make self["password"] (or self[1]) = "my0dear1secret" work.
243 i = None
244 try:
245 i = int(name)
246 except: pass
247 if i is not None:
248 spec = self.COL_SPEC[i]
249 self.__data[spec["name"]] = spec["type"](value)
250 return None
251 if name not in self.__data:
252 raise AttributeError, "unknown field name '%s'" % name
253 self.__data[name] = self.COL_SPEC_DICT[name]["type"](value)
255 def __setattr__(self, name, value):
257 Make self.password = 'my0dear1secret' work.
259 if name == "_TableRowData__data":
260 return super(TableRowData, self).__setattr__(name, value)
261 return self.__setitem__(name, value)
263 def format(self):
265 Format this row of data for human eyes.
267 lines = []
268 maxlen = max(len(s["caption"]) for s in self.COL_SPEC)
269 for spec in self.COL_SPEC:
270 lines.append(("%%-%us%%r" % (maxlen + 2)) %
271 (spec["caption"] + ":",
272 self.__data[spec["name"]]))
273 lines.append("")
274 return "\n".join(lines)
277 class ServerInfo(TableRowData):
279 Information about a Galcon server.
282 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
283 bots_ok; bool; (Bots Ok)
284 allowed; str; Ranks
285 owner; str; Owner
286 version; str; Version
287 num; int; Players
288 name; str; Name
289 pwd_protected; bool; Protected
290 status; str; (Status)
291 addr; str; (IP Address)
292 port; int; (Port); 9031
293 secret; str; (Secret Hash)
294 passwd; str; (Password)
295 """)
298 class UserInfo(TableRowData):
300 Information about a Galcon user.
303 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
304 email; str; Email
305 name; str; Name
306 passwd; str; Password
307 platform; str; Platform; linux2
308 version; str; Version; 1.2.0
309 """)
312 class PlayerPlayersInfo(TableRowData):
314 Information about a Galcon player.
317 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
318 num; int; Number
319 name; str; Name
320 junk1; str; Junk 1
321 junk2; str; Junk 2
322 state; str; State
323 junk3; str; Junk 3
324 junk4; str; Junk 4
325 color; str; Color
326 """)
329 class PlayerStartInfo(TableRowData):
331 In-game information about a Galcon player.
334 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
335 num; int; Number
336 name; str; Name
337 junk1; str; Junk 1
338 color; str; Color
339 """)
342 class OptionB64Info(TableRowData):
344 In-game, base64-encoded information about Galcon "options".
347 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
348 junk1; float; Junk 1
349 junk2; float; Junk 2
350 junk3; float; Junk 3
351 junk4; float; Junk 4
352 junk5; float; Junk 5
353 junk6; float; Junk 6
354 """)
357 class PlayerB64Info(TableRowData):
359 In-game, base64-encoded information about a Galcon player.
362 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
363 num; int; Number
364 name; str; Name
365 junk1; str; Junk 1
366 color; str; Color
367 """)
370 class PlanetB64Info(TableRowData):
372 (In-game) information about a Galcon planet.
375 COL_SPEC, COL_SPEC_DICT = TableRowData.build_col_spec_from_string("""
376 num; int; Number; 0
377 x; int; X Position; 0
378 y; int; Y Position; 0
379 owner_num; int; Owner (Number); 0
380 prod; int; Production; 0
381 troops; int; Troops; 0
382 junk1; str; Junk 1; 0
383 junk2; str; Junk 2; 0
384 junk3; str; Junk 3; 0
385 junk4; str; Junk 4; 0
386 junk5; str; Junk 5; 0
387 junk6; str; Junk 6; 0
388 junk7; str; Junk 7; 0
389 junk8; str; Junk 8; 0
390 junk9; str; Junk 9; 0
391 """)