106 If :file:`.hg` directory is found into the cubicweb package, there are |
106 If :file:`.hg` directory is found into the cubicweb package, there are |
107 specific resource rules. |
107 specific resource rules. |
108 |
108 |
109 `<CW_SOFTWARE_ROOT>` is the source checkout's ``cubicweb`` directory: |
109 `<CW_SOFTWARE_ROOT>` is the source checkout's ``cubicweb`` directory: |
110 |
110 |
111 * main cubes directory is `<CW_SOFTWARE_ROOT>/../../cubes`. You can specify |
|
112 another one with :envvar:`CW_INSTANCES_DIR` environment variable or simply |
|
113 add some other directories by using :envvar:`CW_CUBES_PATH` |
|
114 |
|
115 * cubicweb migration files are searched in `<CW_SOFTWARE_ROOT>/misc/migration` |
111 * cubicweb migration files are searched in `<CW_SOFTWARE_ROOT>/misc/migration` |
116 instead of `<INSTALL_PREFIX>/share/cubicweb/migration/`. |
112 instead of `<INSTALL_PREFIX>/share/cubicweb/migration/`. |
117 |
113 |
118 |
114 |
119 Development Mode (virtualenv) |
115 Development Mode (virtualenv) |
179 |
170 |
180 import importlib |
171 import importlib |
181 import logging |
172 import logging |
182 import logging.config |
173 import logging.config |
183 import os |
174 import os |
184 from os.path import (exists, join, expanduser, abspath, normpath, |
175 from os.path import (exists, join, expanduser, abspath, |
185 basename, isdir, dirname, splitext, realpath) |
176 basename, dirname, splitext, realpath) |
186 import pkgutil |
177 import pkgutil |
187 import pkg_resources |
178 import pkg_resources |
188 import re |
|
189 from smtplib import SMTP |
179 from smtplib import SMTP |
190 import stat |
180 import stat |
191 import sys |
181 import sys |
192 from threading import Lock |
182 from threading import Lock |
193 from warnings import filterwarnings |
183 from warnings import filterwarnings |
194 |
184 |
195 from six import text_type |
185 from six import text_type |
196 |
186 |
197 from logilab.common.decorators import cached, classproperty |
187 from logilab.common.decorators import cached |
198 from logilab.common.logging_ext import set_log_methods, init_log |
188 from logilab.common.logging_ext import set_log_methods, init_log |
199 from logilab.common.configuration import (Configuration, Method, |
189 from logilab.common.configuration import (Configuration, Method, |
200 ConfigurationMixIn, merge_options, |
190 ConfigurationMixIn, merge_options, |
201 _validate as lgc_validate) |
191 _validate as lgc_validate) |
202 |
192 |
370 mode = os.environ.get('CW_MODE', 'user') |
360 mode = os.environ.get('CW_MODE', 'user') |
371 else: |
361 else: |
372 mode = os.environ.get('CW_MODE', 'system') |
362 mode = os.environ.get('CW_MODE', 'system') |
373 assert mode in ('system', 'user'), '"CW_MODE" should be either "user" or "system"' |
363 assert mode in ('system', 'user'), '"CW_MODE" should be either "user" or "system"' |
374 |
364 |
375 _CUBES_DIR = join(_INSTALL_PREFIX, 'share', 'cubicweb', 'cubes') |
|
376 assert _CUBES_DIR # XXX only meaningful if CW_CUBES_DIR is not set |
|
377 CUBES_DIR = realpath(abspath(os.environ.get('CW_CUBES_DIR', _CUBES_DIR))) |
|
378 CUBES_PATH = os.environ.get('CW_CUBES_PATH', '').split(os.pathsep) |
|
379 |
|
380 options = ( |
365 options = ( |
381 ('log-threshold', |
366 ('log-threshold', |
382 {'type' : 'string', # XXX use a dedicated type? |
367 {'type' : 'string', # XXX use a dedicated type? |
383 'default': 'WARNING', |
368 'default': 'WARNING', |
384 'help': 'server\'s log level', |
369 'help': 'server\'s log level', |
467 if not modname.startswith('cubicweb_'): |
452 if not modname.startswith('cubicweb_'): |
468 cls.warning('entry point %s does not appear to be a cube', |
453 cls.warning('entry point %s does not appear to be a cube', |
469 entry_point) |
454 entry_point) |
470 continue |
455 continue |
471 cubes.add(modname) |
456 cubes.add(modname) |
472 # Legacy cubes. |
|
473 for directory in cls.cubes_search_path(): |
|
474 if not exists(directory): |
|
475 cls.error('unexistant directory in cubes search path: %s' |
|
476 % directory) |
|
477 continue |
|
478 for cube in os.listdir(directory): |
|
479 if cube == 'shared': |
|
480 continue |
|
481 if not re.match('[_A-Za-z][_A-Za-z0-9]*$', cube): |
|
482 continue # skip invalid python package name |
|
483 if cube == 'pyramid': |
|
484 cls._warn_pyramid_cube() |
|
485 continue |
|
486 cubedir = join(directory, cube) |
|
487 if isdir(cubedir) and exists(join(cubedir, '__init__.py')): |
|
488 cubes.add(cube) |
|
489 |
457 |
490 def sortkey(cube): |
458 def sortkey(cube): |
491 """Preserve sorting with "cubicweb_" prefix.""" |
459 """Preserve sorting with "cubicweb_" prefix.""" |
492 prefix = 'cubicweb_' |
460 prefix = 'cubicweb_' |
493 if cube.startswith(prefix): |
461 if cube.startswith(prefix): |
498 return cube |
466 return cube |
499 |
467 |
500 return sorted(cubes, key=sortkey) |
468 return sorted(cubes, key=sortkey) |
501 |
469 |
502 @classmethod |
470 @classmethod |
503 def cubes_search_path(cls): |
|
504 """return the path of directories where cubes should be searched""" |
|
505 path = [realpath(abspath(normpath(directory))) for directory in cls.CUBES_PATH |
|
506 if directory.strip() and exists(directory.strip())] |
|
507 if not cls.CUBES_DIR in path and exists(cls.CUBES_DIR): |
|
508 path.append(cls.CUBES_DIR) |
|
509 return path |
|
510 |
|
511 @classproperty |
|
512 def extrapath(cls): |
|
513 extrapath = {} |
|
514 for cubesdir in cls.cubes_search_path(): |
|
515 if cubesdir != cls.CUBES_DIR: |
|
516 extrapath[cubesdir] = 'cubes' |
|
517 return extrapath |
|
518 |
|
519 @classmethod |
|
520 def cube_dir(cls, cube): |
471 def cube_dir(cls, cube): |
521 """return the cube directory for the given cube id, raise |
472 """return the cube directory for the given cube id, raise |
522 `ConfigurationError` if it doesn't exist |
473 `ConfigurationError` if it doesn't exist |
523 """ |
474 """ |
524 pkgname = _cube_pkgname(cube) |
475 pkgname = _cube_pkgname(cube) |
525 loader = pkgutil.find_loader(pkgname) |
476 loader = pkgutil.find_loader(pkgname) |
526 if loader: |
477 if loader: |
527 return dirname(loader.get_filename()) |
478 return dirname(loader.get_filename()) |
528 # Legacy cubes. |
|
529 for directory in cls.cubes_search_path(): |
|
530 cubedir = join(directory, cube) |
|
531 if exists(cubedir): |
|
532 return cubedir |
|
533 msg = 'no module %(pkg)s in search path nor cube %(cube)r in %(path)s' |
479 msg = 'no module %(pkg)s in search path nor cube %(cube)r in %(path)s' |
534 raise ConfigurationError(msg % {'cube': cube, |
480 raise ConfigurationError(msg % {'cube': cube, |
535 'pkg': _cube_pkgname(cube), |
481 'pkg': _cube_pkgname(cube)}) |
536 'path': cls.cubes_search_path()}) |
|
537 |
482 |
538 @classmethod |
483 @classmethod |
539 def cube_migration_scripts_dir(cls, cube): |
484 def cube_migration_scripts_dir(cls, cube): |
540 """cube migration scripts directory""" |
485 """cube migration scripts directory""" |
541 return join(cls.cube_dir(cube), 'migration') |
486 return join(cls.cube_dir(cube), 'migration') |
542 |
487 |
543 @classmethod |
488 @classmethod |
544 def cube_pkginfo(cls, cube): |
489 def cube_pkginfo(cls, cube): |
545 """return the information module for the given cube""" |
490 """return the information module for the given cube""" |
|
491 cube = CW_MIGRATION_MAP.get(cube, cube) |
546 pkgname = _cube_pkgname(cube) |
492 pkgname = _cube_pkgname(cube) |
547 try: |
493 return importlib.import_module('%s.__pkginfo__' % pkgname) |
548 return importlib.import_module('%s.__pkginfo__' % pkgname) |
|
549 except ImportError: |
|
550 cube = CW_MIGRATION_MAP.get(cube, cube) |
|
551 try: |
|
552 parent = __import__('cubes.%s.__pkginfo__' % cube) |
|
553 return getattr(parent, cube).__pkginfo__ |
|
554 except Exception as ex: |
|
555 raise ConfigurationError( |
|
556 'unable to find packaging information for cube %s (%s: %s)' |
|
557 % (cube, ex.__class__.__name__, ex)) |
|
558 |
494 |
559 @classmethod |
495 @classmethod |
560 def cube_version(cls, cube): |
496 def cube_version(cls, cube): |
561 """return the version of the cube located in the given directory |
497 """return the version of the cube located in the given directory |
562 """ |
498 """ |
650 return ordered_nodes(graph) |
586 return ordered_nodes(graph) |
651 except UnorderableGraph as ex: |
587 except UnorderableGraph as ex: |
652 raise ConfigurationError(ex) |
588 raise ConfigurationError(ex) |
653 |
589 |
654 @classmethod |
590 @classmethod |
655 def cls_adjust_sys_path(cls): |
|
656 """update python path if necessary""" |
|
657 import cubes |
|
658 cubes.__path__ = cls.cubes_search_path() |
|
659 |
|
660 @classmethod |
|
661 def load_available_configs(cls): |
591 def load_available_configs(cls): |
662 for confmod in ('web.webconfig', |
592 for confmod in ('web.webconfig', |
663 'server.serverconfig', 'pyramid.config'): |
593 'server.serverconfig', 'pyramid.config'): |
664 try: |
594 try: |
665 __import__('cubicweb.%s' % confmod) |
595 __import__('cubicweb.%s' % confmod) |
667 cls.warning('failed to load config module %s (%s)', |
597 cls.warning('failed to load config module %s (%s)', |
668 confmod, exc) |
598 confmod, exc) |
669 |
599 |
670 @classmethod |
600 @classmethod |
671 def load_cwctl_plugins(cls): |
601 def load_cwctl_plugins(cls): |
672 cls.cls_adjust_sys_path() |
|
673 for ctlmod in ('web.webctl', 'server.serverctl', |
602 for ctlmod in ('web.webctl', 'server.serverctl', |
674 'devtools.devctl', 'pyramid.pyramidctl'): |
603 'devtools.devctl', 'pyramid.pyramidctl'): |
675 try: |
604 try: |
676 __import__('cubicweb.%s' % ctlmod) |
605 __import__('cubicweb.%s' % ctlmod) |
677 except ImportError as exc: |
606 except ImportError as exc: |
681 cls.info('loaded cubicweb-ctl plugin %s', ctlmod) |
610 cls.info('loaded cubicweb-ctl plugin %s', ctlmod) |
682 for cube in cls.available_cubes(): |
611 for cube in cls.available_cubes(): |
683 cubedir = cls.cube_dir(cube) |
612 cubedir = cls.cube_dir(cube) |
684 pluginfile = join(cubedir, 'ccplugin.py') |
613 pluginfile = join(cubedir, 'ccplugin.py') |
685 initfile = join(cubedir, '__init__.py') |
614 initfile = join(cubedir, '__init__.py') |
686 if cube.startswith('cubicweb_'): |
615 pkgname = _cube_pkgname(cube) |
687 pkgname = cube |
|
688 else: |
|
689 pkgname = 'cubes.%s' % cube |
|
690 if exists(pluginfile): |
616 if exists(pluginfile): |
691 try: |
617 try: |
692 __import__(pkgname + '.ccplugin') |
618 __import__(pkgname + '.ccplugin') |
693 cls.info('loaded cubicweb-ctl plugin from %s', cube) |
619 cls.info('loaded cubicweb-ctl plugin from %s', cube) |
694 except Exception: |
620 except Exception: |
850 self._warn_pyramid_cube() |
772 self._warn_pyramid_cube() |
851 cubes.remove('pyramid') |
773 cubes.remove('pyramid') |
852 self._cubes = self.reorder_cubes(cubes) |
774 self._cubes = self.reorder_cubes(cubes) |
853 # load cubes'__init__.py file first |
775 # load cubes'__init__.py file first |
854 for cube in cubes: |
776 for cube in cubes: |
855 try: |
777 importlib.import_module(_cube_pkgname(cube)) |
856 importlib.import_module(_cube_pkgname(cube)) |
|
857 except ImportError: |
|
858 # Legacy cube. |
|
859 __import__('cubes.%s' % cube) |
|
860 self.load_site_cubicweb() |
778 self.load_site_cubicweb() |
861 |
779 |
862 def cubes(self): |
780 def cubes(self): |
863 """return the list of cubes used by this instance |
781 """return the list of cubes used by this instance |
864 |
782 |