annotate pylons_app/lib/db_manage.py @ 262:a4be1ebb214f

updated db manage with some defaults
author Marcin Kuzminski <marcin@python-works.com>
date Sun, 06 Jun 2010 23:33:26 +0200
parents 3782a6d698af
children c3aeb819d9ab
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
252
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
1 #!/usr/bin/env python
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
2 # encoding: utf-8
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
3 # database managment for hg app
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
5
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
6 # This program is free software; you can redistribute it and/or
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
7 # modify it under the terms of the GNU General Public License
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
8 # as published by the Free Software Foundation; version 2
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
9 # of the License or (at your opinion) any later version of the license.
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
10 #
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
11 # This program is distributed in the hope that it will be useful,
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
14 # GNU General Public License for more details.
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
15 #
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
16 # You should have received a copy of the GNU General Public License
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
17 # along with this program; if not, write to the Free Software
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
19 # MA 02110-1301, USA.
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
20
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
21 """
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
22 Created on April 10, 2010
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
23 database managment and creation for hg app
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
24 @author: marcink
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
25 """
3782a6d698af licensing updates, code cleanups
Marcin Kuzminski <marcin@python-works.com>
parents: 249
diff changeset
26
239
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
27 from os.path import dirname as dn, join as jn
249
bad9ccac26b7 fixed import errors on db_manage
Marcin Kuzminski <marcin@python-works.com>
parents: 243
diff changeset
28 import os
bad9ccac26b7 fixed import errors on db_manage
Marcin Kuzminski <marcin@python-works.com>
parents: 243
diff changeset
29 import sys
bad9ccac26b7 fixed import errors on db_manage
Marcin Kuzminski <marcin@python-works.com>
parents: 243
diff changeset
30 ROOT = dn(dn(dn(os.path.realpath(__file__))))
bad9ccac26b7 fixed import errors on db_manage
Marcin Kuzminski <marcin@python-works.com>
parents: 243
diff changeset
31 sys.path.append(ROOT)
bad9ccac26b7 fixed import errors on db_manage
Marcin Kuzminski <marcin@python-works.com>
parents: 243
diff changeset
32
239
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
33 from pylons_app.lib.auth import get_crypt_password
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
34 from pylons_app.model import init_model
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
35 from pylons_app.model.db import User, Permission
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
36 from pylons_app.model.meta import Session, Base
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
37 from sqlalchemy.engine import create_engine
59
736078908f37 added db_manage script
Marcin Kuzminski <marcin@python-blog.com>
parents:
diff changeset
38 import logging
736078908f37 added db_manage script
Marcin Kuzminski <marcin@python-blog.com>
parents:
diff changeset
39
229
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
40 log = logging.getLogger('db manage')
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
41 log.setLevel(logging.DEBUG)
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
42 console_handler = logging.StreamHandler()
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
43 console_handler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)03d"
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
44 " %(levelname)-5.5s [%(name)s] %(message)s"))
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
45 log.addHandler(console_handler)
66
163464441e0d updated db manage script for creating interactive admin account and db
Marcin Kuzminski <marcin@python-blog.com>
parents: 59
diff changeset
46
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
47 class DbManage(object):
229
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
48 def __init__(self, log_sql):
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
49 self.dbname = 'hg_app.db'
234
a0116e944da1 changed naming convention for db modules.
Marcin Kuzminski <marcin@python-works.com>
parents: 229
diff changeset
50 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
229
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
51 engine = create_engine(dburi, echo=log_sql)
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
52 init_model(engine)
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
53 self.sa = Session()
243
9d64df490248 fixed bug when there was no dbfile, and dbmanage raise an exception
Marcin Kuzminski <marcin@python-works.com>
parents: 240
diff changeset
54 self.db_exists = False
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
55
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
56 def check_for_db(self, override):
229
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
57 log.info('checking for exisiting db')
234
a0116e944da1 changed naming convention for db modules.
Marcin Kuzminski <marcin@python-works.com>
parents: 229
diff changeset
58 if os.path.isfile(jn(ROOT, self.dbname)):
243
9d64df490248 fixed bug when there was no dbfile, and dbmanage raise an exception
Marcin Kuzminski <marcin@python-works.com>
parents: 240
diff changeset
59 self.db_exists = True
229
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
60 log.info('database exisist')
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
61 if not override:
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
62 raise Exception('database already exists')
229
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
63
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
64 def create_tables(self, override=False):
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
65 """
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
66 Create a auth database
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
67 """
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
68 self.check_for_db(override)
229
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
69 if override:
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
70 log.info("database exisist and it's going to be destroyed")
243
9d64df490248 fixed bug when there was no dbfile, and dbmanage raise an exception
Marcin Kuzminski <marcin@python-works.com>
parents: 240
diff changeset
71 if self.db_exists:
9d64df490248 fixed bug when there was no dbfile, and dbmanage raise an exception
Marcin Kuzminski <marcin@python-works.com>
parents: 240
diff changeset
72 os.remove(jn(ROOT, self.dbname))
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
73 Base.metadata.create_all(checkfirst=override)
229
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
74 log.info('Created tables for %s', self.dbname)
59
736078908f37 added db_manage script
Marcin Kuzminski <marcin@python-blog.com>
parents:
diff changeset
75
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
76 def admin_prompt(self):
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
77 import getpass
229
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
78 username = raw_input('Specify admin username:')
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
79 password = getpass.getpass('Specify admin password:')
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
80 self.create_user(username, password, True)
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
81
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
82 def create_user(self, username, password, admin=False):
229
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
83 log.info('creating administrator user %s', username)
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
84
234
a0116e944da1 changed naming convention for db modules.
Marcin Kuzminski <marcin@python-works.com>
parents: 229
diff changeset
85 new_user = User()
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
86 new_user.username = username
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
87 new_user.password = get_crypt_password(password)
262
a4be1ebb214f updated db manage with some defaults
Marcin Kuzminski <marcin@python-works.com>
parents: 252
diff changeset
88 new_user.username = 'Admin'
a4be1ebb214f updated db manage with some defaults
Marcin Kuzminski <marcin@python-works.com>
parents: 252
diff changeset
89 new_user.lastname = 'Admin'
a4be1ebb214f updated db manage with some defaults
Marcin Kuzminski <marcin@python-works.com>
parents: 252
diff changeset
90 new_user.lastname = 'admin@localhost'
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
91 new_user.admin = admin
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
92 new_user.active = True
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
93
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
94 try:
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
95 self.sa.add(new_user)
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
96 self.sa.commit()
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
97 except:
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
98 self.sa.rollback()
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
99 raise
66
163464441e0d updated db manage script for creating interactive admin account and db
Marcin Kuzminski <marcin@python-blog.com>
parents: 59
diff changeset
100
239
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
101 def create_permissions(self):
240
7c4fa2a66195 small fixes
Marcin Kuzminski <marcin@python-works.com>
parents: 239
diff changeset
102 #module.(access|create|change|delete)_[name]
7c4fa2a66195 small fixes
Marcin Kuzminski <marcin@python-works.com>
parents: 239
diff changeset
103 perms = [('admin.access_home', 'Access to admin user view'),
239
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
104
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
105 ]
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
106
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
107 for p in perms:
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
108 new_perm = Permission()
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
109 new_perm.permission_name = p[0]
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
110 new_perm.permission_longname = p[1]
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
111 try:
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
112 self.sa.add(new_perm)
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
113 self.sa.commit()
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
114 except:
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
115 self.sa.rollback()
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
116 raise
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
117
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
118
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
119
66
163464441e0d updated db manage script for creating interactive admin account and db
Marcin Kuzminski <marcin@python-blog.com>
parents: 59
diff changeset
120 if __name__ == '__main__':
229
69a29242ba61 db manage added more logging, set custom logger and add optional print sql statments
Marcin Kuzminski <marcin@python-works.com>
parents: 228
diff changeset
121 dbmanage = DbManage(log_sql=True)
226
c6526b7531e9 rewritten db manage script to use sqlalchemy. Fixed sqlalchemy models to more generic.
Marcin Kuzminski <marcin@python-works.com>
parents: 88
diff changeset
122 dbmanage.create_tables(override=True)
239
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
123 dbmanage.admin_prompt()
b18f89d6d17f Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
Marcin Kuzminski <marcin@python-works.com>
parents: 234
diff changeset
124 dbmanage.create_permissions()
66
163464441e0d updated db manage script for creating interactive admin account and db
Marcin Kuzminski <marcin@python-blog.com>
parents: 59
diff changeset
125
163464441e0d updated db manage script for creating interactive admin account and db
Marcin Kuzminski <marcin@python-blog.com>
parents: 59
diff changeset
126