##// END OF EJS Templates
removed leftover print
marcink -
r1243:e82d6aaa beta
parent child Browse files
Show More
@@ -1,707 +1,706 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Utilities library for RhodeCode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import datetime
29 29 import traceback
30 30 import paste
31 31 import beaker
32 32
33 33 from paste.script.command import Command, BadCommand
34 34
35 35 from UserDict import DictMixin
36 36
37 37 from mercurial import ui, config, hg
38 38 from mercurial.error import RepoError
39 39
40 40 from webhelpers.text import collapse, remove_formatting, strip_tags
41 41
42 42 from vcs.backends.base import BaseChangeset
43 43 from vcs.utils.lazy import LazyProperty
44 44
45 45 from rhodecode.model import meta
46 46 from rhodecode.model.caching_query import FromCache
47 47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group
48 48 from rhodecode.model.repo import RepoModel
49 49 from rhodecode.model.user import UserModel
50 50
51 51 log = logging.getLogger(__name__)
52 52
53 53
54 54 def recursive_replace(str, replace=' '):
55 55 """Recursive replace of given sign to just one instance
56 56
57 57 :param str: given string
58 58 :param replace: char to find and replace multiple instances
59 59
60 60 Examples::
61 61 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
62 62 'Mighty-Mighty-Bo-sstones'
63 63 """
64 64
65 65 if str.find(replace * 2) == -1:
66 66 return str
67 67 else:
68 68 str = str.replace(replace * 2, replace)
69 69 return recursive_replace(str, replace)
70 70
71 71
72 72 def repo_name_slug(value):
73 73 """Return slug of name of repository
74 74 This function is called on each creation/modification
75 75 of repository to prevent bad names in repo
76 76 """
77 77
78 78 slug = remove_formatting(value)
79 79 slug = strip_tags(slug)
80 80
81 81 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
82 82 slug = slug.replace(c, '-')
83 83 slug = recursive_replace(slug, '-')
84 84 slug = collapse(slug, '-')
85 85 return slug
86 86
87 87
88 88 def get_repo_slug(request):
89 89 return request.environ['pylons.routes_dict'].get('repo_name')
90 90
91 91
92 92 def action_logger(user, action, repo, ipaddr='', sa=None):
93 93 """
94 94 Action logger for various actions made by users
95 95
96 96 :param user: user that made this action, can be a unique username string or
97 97 object containing user_id attribute
98 98 :param action: action to log, should be on of predefined unique actions for
99 99 easy translations
100 100 :param repo: string name of repository or object containing repo_id,
101 101 that action was made on
102 102 :param ipaddr: optional ip address from what the action was made
103 103 :param sa: optional sqlalchemy session
104 104
105 105 """
106 106
107 107 if not sa:
108 108 sa = meta.Session()
109 109
110 110 try:
111 111 um = UserModel()
112 112 if hasattr(user, 'user_id'):
113 113 user_obj = user
114 114 elif isinstance(user, basestring):
115 115 user_obj = um.get_by_username(user, cache=False)
116 116 else:
117 117 raise Exception('You have to provide user object or username')
118 118
119 119 rm = RepoModel()
120 120 if hasattr(repo, 'repo_id'):
121 121 repo_obj = rm.get(repo.repo_id, cache=False)
122 122 repo_name = repo_obj.repo_name
123 123 elif isinstance(repo, basestring):
124 124 repo_name = repo.lstrip('/')
125 125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
126 126 else:
127 127 raise Exception('You have to provide repository to action logger')
128 128
129 129 user_log = UserLog()
130 130 user_log.user_id = user_obj.user_id
131 131 user_log.action = action
132 132
133 133 user_log.repository_id = repo_obj.repo_id
134 134 user_log.repository_name = repo_name
135 135
136 136 user_log.action_date = datetime.datetime.now()
137 137 user_log.user_ip = ipaddr
138 138 sa.add(user_log)
139 139 sa.commit()
140 140
141 141 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
142 142 except:
143 143 log.error(traceback.format_exc())
144 144 sa.rollback()
145 145
146 146
147 147 def get_repos(path, recursive=False):
148 148 """
149 149 Scans given path for repos and return (name,(type,path)) tuple
150 150
151 151 :param path: path to scann for repositories
152 152 :param recursive: recursive search and return names with subdirs in front
153 153 """
154 154 from vcs.utils.helpers import get_scm
155 155 from vcs.exceptions import VCSError
156 156
157 157 if path.endswith(os.sep):
158 158 #remove ending slash for better results
159 159 path = path[:-1]
160 160
161 161 def _get_repos(p):
162 162 if not os.access(p, os.W_OK):
163 163 return
164 164 for dirpath in os.listdir(p):
165 print dirpath
166 165 if os.path.isfile(os.path.join(p, dirpath)):
167 166 continue
168 167 cur_path = os.path.join(p, dirpath)
169 168 try:
170 169 scm_info = get_scm(cur_path)
171 170 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
172 171 except VCSError:
173 172 if not recursive:
174 173 continue
175 174 #check if this dir containts other repos for recursive scan
176 175 rec_path = os.path.join(p, dirpath)
177 176 if os.path.isdir(rec_path):
178 177 for inner_scm in _get_repos(rec_path):
179 178 yield inner_scm
180 179
181 180 return _get_repos(path)
182 181
183 182
184 183 def check_repo_fast(repo_name, base_path):
185 184 """
186 185 Check given path for existence of directory
187 186 :param repo_name:
188 187 :param base_path:
189 188
190 189 :return False: if this directory is present
191 190 """
192 191 if os.path.isdir(os.path.join(base_path, repo_name)):
193 192 return False
194 193 return True
195 194
196 195
197 196 def check_repo(repo_name, base_path, verify=True):
198 197
199 198 repo_path = os.path.join(base_path, repo_name)
200 199
201 200 try:
202 201 if not check_repo_fast(repo_name, base_path):
203 202 return False
204 203 r = hg.repository(ui.ui(), repo_path)
205 204 if verify:
206 205 hg.verify(r)
207 206 #here we hnow that repo exists it was verified
208 207 log.info('%s repo is already created', repo_name)
209 208 return False
210 209 except RepoError:
211 210 #it means that there is no valid repo there...
212 211 log.info('%s repo is free for creation', repo_name)
213 212 return True
214 213
215 214
216 215 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
217 216 while True:
218 217 ok = raw_input(prompt)
219 218 if ok in ('y', 'ye', 'yes'):
220 219 return True
221 220 if ok in ('n', 'no', 'nop', 'nope'):
222 221 return False
223 222 retries = retries - 1
224 223 if retries < 0:
225 224 raise IOError
226 225 print complaint
227 226
228 227 #propagated from mercurial documentation
229 228 ui_sections = ['alias', 'auth',
230 229 'decode/encode', 'defaults',
231 230 'diff', 'email',
232 231 'extensions', 'format',
233 232 'merge-patterns', 'merge-tools',
234 233 'hooks', 'http_proxy',
235 234 'smtp', 'patch',
236 235 'paths', 'profiling',
237 236 'server', 'trusted',
238 237 'ui', 'web', ]
239 238
240 239
241 240 def make_ui(read_from='file', path=None, checkpaths=True):
242 241 """A function that will read python rc files or database
243 242 and make an mercurial ui object from read options
244 243
245 244 :param path: path to mercurial config file
246 245 :param checkpaths: check the path
247 246 :param read_from: read from 'file' or 'db'
248 247 """
249 248
250 249 baseui = ui.ui()
251 250
252 251 #clean the baseui object
253 252 baseui._ocfg = config.config()
254 253 baseui._ucfg = config.config()
255 254 baseui._tcfg = config.config()
256 255
257 256 if read_from == 'file':
258 257 if not os.path.isfile(path):
259 258 log.warning('Unable to read config file %s' % path)
260 259 return False
261 260 log.debug('reading hgrc from %s', path)
262 261 cfg = config.config()
263 262 cfg.read(path)
264 263 for section in ui_sections:
265 264 for k, v in cfg.items(section):
266 265 log.debug('settings ui from file[%s]%s:%s', section, k, v)
267 266 baseui.setconfig(section, k, v)
268 267
269 268 elif read_from == 'db':
270 269 sa = meta.Session()
271 270 ret = sa.query(RhodeCodeUi)\
272 271 .options(FromCache("sql_cache_short",
273 272 "get_hg_ui_settings")).all()
274 273
275 274 hg_ui = ret
276 275 for ui_ in hg_ui:
277 276 if ui_.ui_active:
278 277 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
279 278 ui_.ui_key, ui_.ui_value)
280 279 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
281 280
282 281 meta.Session.remove()
283 282 return baseui
284 283
285 284
286 285 def set_rhodecode_config(config):
287 286 """Updates pylons config with new settings from database
288 287
289 288 :param config:
290 289 """
291 290 from rhodecode.model.settings import SettingsModel
292 291 hgsettings = SettingsModel().get_app_settings()
293 292
294 293 for k, v in hgsettings.items():
295 294 config[k] = v
296 295
297 296
298 297 def invalidate_cache(cache_key, *args):
299 298 """Puts cache invalidation task into db for
300 299 further global cache invalidation
301 300 """
302 301
303 302 from rhodecode.model.scm import ScmModel
304 303
305 304 if cache_key.startswith('get_repo_cached_'):
306 305 name = cache_key.split('get_repo_cached_')[-1]
307 306 ScmModel().mark_for_invalidation(name)
308 307
309 308
310 309 class EmptyChangeset(BaseChangeset):
311 310 """
312 311 An dummy empty changeset. It's possible to pass hash when creating
313 312 an EmptyChangeset
314 313 """
315 314
316 315 def __init__(self, cs='0' * 40, repo=None):
317 316 self._empty_cs = cs
318 317 self.revision = -1
319 318 self.message = ''
320 319 self.author = ''
321 320 self.date = ''
322 321 self.repository = repo
323 322
324 323 @LazyProperty
325 324 def raw_id(self):
326 325 """Returns raw string identifying this changeset, useful for web
327 326 representation.
328 327 """
329 328
330 329 return self._empty_cs
331 330
332 331 @LazyProperty
333 332 def short_id(self):
334 333 return self.raw_id[:12]
335 334
336 335 def get_file_changeset(self, path):
337 336 return self
338 337
339 338 def get_file_content(self, path):
340 339 return u''
341 340
342 341 def get_file_size(self, path):
343 342 return 0
344 343
345 344
346 345 def map_groups(groups):
347 346 """Checks for groups existence, and creates groups structures.
348 347 It returns last group in structure
349 348
350 349 :param groups: list of groups structure
351 350 """
352 351 sa = meta.Session()
353 352
354 353 parent = None
355 354 group = None
356 355 for lvl, group_name in enumerate(groups[:-1]):
357 356 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
358 357
359 358 if group is None:
360 359 group = Group(group_name, parent)
361 360 sa.add(group)
362 361 sa.commit()
363 362
364 363 parent = group
365 364
366 365 return group
367 366
368 367
369 368 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
370 369 """maps all repos given in initial_repo_list, non existing repositories
371 370 are created, if remove_obsolete is True it also check for db entries
372 371 that are not in initial_repo_list and removes them.
373 372
374 373 :param initial_repo_list: list of repositories found by scanning methods
375 374 :param remove_obsolete: check for obsolete entries in database
376 375 """
377 376
378 377 sa = meta.Session()
379 378 rm = RepoModel()
380 379 user = sa.query(User).filter(User.admin == True).first()
381 380 added = []
382 381 for name, repo in initial_repo_list.items():
383 382 group = map_groups(name.split('/'))
384 383 if not rm.get_by_repo_name(name, cache=False):
385 384 log.info('repository %s not found creating default', name)
386 385 added.append(name)
387 386 form_data = {
388 387 'repo_name': name,
389 388 'repo_type': repo.alias,
390 389 'description': repo.description \
391 390 if repo.description != 'unknown' else \
392 391 '%s repository' % name,
393 392 'private': False,
394 393 'group_id': getattr(group, 'group_id', None)
395 394 }
396 395 rm.create(form_data, user, just_db=True)
397 396
398 397 removed = []
399 398 if remove_obsolete:
400 399 #remove from database those repositories that are not in the filesystem
401 400 for repo in sa.query(Repository).all():
402 401 if repo.repo_name not in initial_repo_list.keys():
403 402 removed.append(repo.repo_name)
404 403 sa.delete(repo)
405 404 sa.commit()
406 405
407 406 return added, removed
408 407
409 408
410 409 class OrderedDict(dict, DictMixin):
411 410
412 411 def __init__(self, *args, **kwds):
413 412 if len(args) > 1:
414 413 raise TypeError('expected at most 1 arguments, got %d' % len(args))
415 414 try:
416 415 self.__end
417 416 except AttributeError:
418 417 self.clear()
419 418 self.update(*args, **kwds)
420 419
421 420 def clear(self):
422 421 self.__end = end = []
423 422 end += [None, end, end] # sentinel node for doubly linked list
424 423 self.__map = {} # key --> [key, prev, next]
425 424 dict.clear(self)
426 425
427 426 def __setitem__(self, key, value):
428 427 if key not in self:
429 428 end = self.__end
430 429 curr = end[1]
431 430 curr[2] = end[1] = self.__map[key] = [key, curr, end]
432 431 dict.__setitem__(self, key, value)
433 432
434 433 def __delitem__(self, key):
435 434 dict.__delitem__(self, key)
436 435 key, prev, next = self.__map.pop(key)
437 436 prev[2] = next
438 437 next[1] = prev
439 438
440 439 def __iter__(self):
441 440 end = self.__end
442 441 curr = end[2]
443 442 while curr is not end:
444 443 yield curr[0]
445 444 curr = curr[2]
446 445
447 446 def __reversed__(self):
448 447 end = self.__end
449 448 curr = end[1]
450 449 while curr is not end:
451 450 yield curr[0]
452 451 curr = curr[1]
453 452
454 453 def popitem(self, last=True):
455 454 if not self:
456 455 raise KeyError('dictionary is empty')
457 456 if last:
458 457 key = reversed(self).next()
459 458 else:
460 459 key = iter(self).next()
461 460 value = self.pop(key)
462 461 return key, value
463 462
464 463 def __reduce__(self):
465 464 items = [[k, self[k]] for k in self]
466 465 tmp = self.__map, self.__end
467 466 del self.__map, self.__end
468 467 inst_dict = vars(self).copy()
469 468 self.__map, self.__end = tmp
470 469 if inst_dict:
471 470 return (self.__class__, (items,), inst_dict)
472 471 return self.__class__, (items,)
473 472
474 473 def keys(self):
475 474 return list(self)
476 475
477 476 setdefault = DictMixin.setdefault
478 477 update = DictMixin.update
479 478 pop = DictMixin.pop
480 479 values = DictMixin.values
481 480 items = DictMixin.items
482 481 iterkeys = DictMixin.iterkeys
483 482 itervalues = DictMixin.itervalues
484 483 iteritems = DictMixin.iteritems
485 484
486 485 def __repr__(self):
487 486 if not self:
488 487 return '%s()' % (self.__class__.__name__,)
489 488 return '%s(%r)' % (self.__class__.__name__, self.items())
490 489
491 490 def copy(self):
492 491 return self.__class__(self)
493 492
494 493 @classmethod
495 494 def fromkeys(cls, iterable, value=None):
496 495 d = cls()
497 496 for key in iterable:
498 497 d[key] = value
499 498 return d
500 499
501 500 def __eq__(self, other):
502 501 if isinstance(other, OrderedDict):
503 502 return len(self) == len(other) and self.items() == other.items()
504 503 return dict.__eq__(self, other)
505 504
506 505 def __ne__(self, other):
507 506 return not self == other
508 507
509 508
510 509 #set cache regions for beaker so celery can utilise it
511 510 def add_cache(settings):
512 511 cache_settings = {'regions': None}
513 512 for key in settings.keys():
514 513 for prefix in ['beaker.cache.', 'cache.']:
515 514 if key.startswith(prefix):
516 515 name = key.split(prefix)[1].strip()
517 516 cache_settings[name] = settings[key].strip()
518 517 if cache_settings['regions']:
519 518 for region in cache_settings['regions'].split(','):
520 519 region = region.strip()
521 520 region_settings = {}
522 521 for key, value in cache_settings.items():
523 522 if key.startswith(region):
524 523 region_settings[key.split('.')[1]] = value
525 524 region_settings['expire'] = int(region_settings.get('expire',
526 525 60))
527 526 region_settings.setdefault('lock_dir',
528 527 cache_settings.get('lock_dir'))
529 528 region_settings.setdefault('data_dir',
530 529 cache_settings.get('data_dir'))
531 530
532 531 if 'type' not in region_settings:
533 532 region_settings['type'] = cache_settings.get('type',
534 533 'memory')
535 534 beaker.cache.cache_regions[region] = region_settings
536 535
537 536
538 537 def get_current_revision():
539 538 """Returns tuple of (number, id) from repository containing this package
540 539 or None if repository could not be found.
541 540 """
542 541
543 542 try:
544 543 from vcs import get_repo
545 544 from vcs.utils.helpers import get_scm
546 545 from vcs.exceptions import RepositoryError, VCSError
547 546 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
548 547 scm = get_scm(repopath)[0]
549 548 repo = get_repo(path=repopath, alias=scm)
550 549 tip = repo.get_changeset()
551 550 return (tip.revision, tip.short_id)
552 551 except (ImportError, RepositoryError, VCSError), err:
553 552 logging.debug("Cannot retrieve rhodecode's revision. Original error "
554 553 "was: %s" % err)
555 554 return None
556 555
557 556
558 557 #==============================================================================
559 558 # TEST FUNCTIONS AND CREATORS
560 559 #==============================================================================
561 560 def create_test_index(repo_location, full_index):
562 561 """Makes default test index
563 562 :param repo_location:
564 563 :param full_index:
565 564 """
566 565 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
567 566 from rhodecode.lib.pidlock import DaemonLock, LockHeld
568 567 import shutil
569 568
570 569 index_location = os.path.join(repo_location, 'index')
571 570 if os.path.exists(index_location):
572 571 shutil.rmtree(index_location)
573 572
574 573 try:
575 574 l = DaemonLock()
576 575 WhooshIndexingDaemon(index_location=index_location,
577 576 repo_location=repo_location)\
578 577 .run(full_index=full_index)
579 578 l.release()
580 579 except LockHeld:
581 580 pass
582 581
583 582
584 583 def create_test_env(repos_test_path, config):
585 584 """Makes a fresh database and
586 585 install test repository into tmp dir
587 586 """
588 587 from rhodecode.lib.db_manage import DbManage
589 588 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
590 589 HG_FORK, GIT_FORK, TESTS_TMP_PATH
591 590 import tarfile
592 591 import shutil
593 592 from os.path import dirname as dn, join as jn, abspath
594 593
595 594 log = logging.getLogger('TestEnvCreator')
596 595 # create logger
597 596 log.setLevel(logging.DEBUG)
598 597 log.propagate = True
599 598 # create console handler and set level to debug
600 599 ch = logging.StreamHandler()
601 600 ch.setLevel(logging.DEBUG)
602 601
603 602 # create formatter
604 603 formatter = logging.Formatter("%(asctime)s - %(name)s -"
605 604 " %(levelname)s - %(message)s")
606 605
607 606 # add formatter to ch
608 607 ch.setFormatter(formatter)
609 608
610 609 # add ch to logger
611 610 log.addHandler(ch)
612 611
613 612 #PART ONE create db
614 613 dbconf = config['sqlalchemy.db1.url']
615 614 log.debug('making test db %s', dbconf)
616 615
617 616 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
618 617 tests=True)
619 618 dbmanage.create_tables(override=True)
620 619 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
621 620 dbmanage.create_default_user()
622 621 dbmanage.admin_prompt()
623 622 dbmanage.create_permissions()
624 623 dbmanage.populate_default_permissions()
625 624
626 625 #PART TWO make test repo
627 626 log.debug('making test vcs repositories')
628 627
629 628 #remove old one from previos tests
630 629 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
631 630
632 631 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
633 632 log.debug('removing %s', r)
634 633 shutil.rmtree(jn(TESTS_TMP_PATH, r))
635 634
636 635 #CREATE DEFAULT HG REPOSITORY
637 636 cur_dir = dn(dn(abspath(__file__)))
638 637 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
639 638 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
640 639 tar.close()
641 640
642 641
643 642 #==============================================================================
644 643 # PASTER COMMANDS
645 644 #==============================================================================
646 645 class BasePasterCommand(Command):
647 646 """
648 647 Abstract Base Class for paster commands.
649 648
650 649 The celery commands are somewhat aggressive about loading
651 650 celery.conf, and since our module sets the `CELERY_LOADER`
652 651 environment variable to our loader, we have to bootstrap a bit and
653 652 make sure we've had a chance to load the pylons config off of the
654 653 command line, otherwise everything fails.
655 654 """
656 655 min_args = 1
657 656 min_args_error = "Please provide a paster config file as an argument."
658 657 takes_config_file = 1
659 658 requires_config_file = True
660 659
661 660 def notify_msg(self, msg, log=False):
662 661 """Make a notification to user, additionally if logger is passed
663 662 it logs this action using given logger
664 663
665 664 :param msg: message that will be printed to user
666 665 :param log: logging instance, to use to additionally log this message
667 666
668 667 """
669 668 if log and isinstance(log, logging):
670 669 log(msg)
671 670
672 671 def run(self, args):
673 672 """
674 673 Overrides Command.run
675 674
676 675 Checks for a config file argument and loads it.
677 676 """
678 677 if len(args) < self.min_args:
679 678 raise BadCommand(
680 679 self.min_args_error % {'min_args': self.min_args,
681 680 'actual_args': len(args)})
682 681
683 682 # Decrement because we're going to lob off the first argument.
684 683 # @@ This is hacky
685 684 self.min_args -= 1
686 685 self.bootstrap_config(args[0])
687 686 self.update_parser()
688 687 return super(BasePasterCommand, self).run(args[1:])
689 688
690 689 def update_parser(self):
691 690 """
692 691 Abstract method. Allows for the class's parser to be updated
693 692 before the superclass's `run` method is called. Necessary to
694 693 allow options/arguments to be passed through to the underlying
695 694 celery command.
696 695 """
697 696 raise NotImplementedError("Abstract Method.")
698 697
699 698 def bootstrap_config(self, conf):
700 699 """
701 700 Loads the pylons configuration.
702 701 """
703 702 from pylons import config as pylonsconfig
704 703
705 704 path_to_ini_file = os.path.realpath(conf)
706 705 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
707 706 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
General Comments 0
You need to be logged in to leave comments. Login now