Path: blob/master/elisp/emacs-for-python/rope-dist/rope/base/project.py
1415 views
import cPickle as pickle1import os2import shutil3import sys4import warnings56import rope.base.fscommands7from rope.base import exceptions, taskhandle, prefs, history, pycore, utils8from rope.base.resourceobserver import *9from rope.base.resources import File, Folder, _ResourceMatcher101112class _Project(object):1314def __init__(self, fscommands):15self.observers = []16self.fscommands = fscommands17self.prefs = prefs.Prefs()18self.data_files = _DataFiles(self)1920def get_resource(self, resource_name):21"""Get a resource in a project.2223`resource_name` is the path of a resource in a project. It is24the path of a resource relative to project root. Project root25folder address is an empty string. If the resource does not26exist a `exceptions.ResourceNotFound` exception would be27raised. Use `get_file()` and `get_folder()` when you need to28get nonexistent `Resource`\s.2930"""31path = self._get_resource_path(resource_name)32if not os.path.exists(path):33raise exceptions.ResourceNotFoundError(34'Resource <%s> does not exist' % resource_name)35elif os.path.isfile(path):36return File(self, resource_name)37elif os.path.isdir(path):38return Folder(self, resource_name)39else:40raise exceptions.ResourceNotFoundError('Unknown resource '41+ resource_name)4243def validate(self, folder):44"""Validate files and folders contained in this folder4546It validates all of the files and folders contained in this47folder if some observers are interested in them.4849"""50for observer in list(self.observers):51observer.validate(folder)5253def add_observer(self, observer):54"""Register a `ResourceObserver`5556See `FilteredResourceObserver`.57"""58self.observers.append(observer)5960def remove_observer(self, observer):61"""Remove a registered `ResourceObserver`"""62if observer in self.observers:63self.observers.remove(observer)6465def do(self, changes, task_handle=taskhandle.NullTaskHandle()):66"""Apply the changes in a `ChangeSet`6768Most of the time you call this function for committing the69changes for a refactoring.70"""71self.history.do(changes, task_handle=task_handle)7273def get_pycore(self):74return self.pycore7576def get_file(self, path):77"""Get the file with `path` (it may not exist)"""78return File(self, path)7980def get_folder(self, path):81"""Get the folder with `path` (it may not exist)"""82return Folder(self, path)8384def is_ignored(self, resource):85return False8687def get_prefs(self):88return self.prefs8990def _get_resource_path(self, name):91pass9293@property94@utils.saveit95def history(self):96return history.History(self)9798@property99@utils.saveit100def pycore(self):101return pycore.PyCore(self)102103def close(self):104warnings.warn('Cannot close a NoProject',105DeprecationWarning, stacklevel=2)106107ropefolder = None108109110class Project(_Project):111"""A Project containing files and folders"""112113def __init__(self, projectroot, fscommands=None,114ropefolder='.ropeproject', **prefs):115"""A rope project116117:parameters:118- `projectroot`: The address of the root folder of the project119- `fscommands`: Implements the file system operations used120by rope; have a look at `rope.base.fscommands`121- `ropefolder`: The name of the folder in which rope stores122project configurations and data. Pass `None` for not using123such a folder at all.124- `prefs`: Specify project preferences. These values125overwrite config file preferences.126127"""128if projectroot != '/':129projectroot = _realpath(projectroot).rstrip('/\\')130self._address = projectroot131self._ropefolder_name = ropefolder132if not os.path.exists(self._address):133os.mkdir(self._address)134elif not os.path.isdir(self._address):135raise exceptions.RopeError('Project root exists and'136' is not a directory')137if fscommands is None:138fscommands = rope.base.fscommands.create_fscommands(self._address)139super(Project, self).__init__(fscommands)140self.ignored = _ResourceMatcher()141self.file_list = _FileListCacher(self)142self.prefs.add_callback('ignored_resources', self.ignored.set_patterns)143if ropefolder is not None:144self.prefs['ignored_resources'] = [ropefolder]145self._init_prefs(prefs)146147def get_files(self):148return self.file_list.get_files()149150def _get_resource_path(self, name):151return os.path.join(self._address, *name.split('/'))152153def _init_ropefolder(self):154if self.ropefolder is not None:155if not self.ropefolder.exists():156self._create_recursively(self.ropefolder)157if not self.ropefolder.has_child('config.py'):158config = self.ropefolder.create_file('config.py')159config.write(self._default_config())160161def _create_recursively(self, folder):162if folder.parent != self.root and not folder.parent.exists():163self._create_recursively(folder.parent)164folder.create()165166def _init_prefs(self, prefs):167run_globals = {}168if self.ropefolder is not None:169config = self.get_file(self.ropefolder.path + '/config.py')170run_globals.update({'__name__': '__main__',171'__builtins__': __builtins__,172'__file__': config.real_path})173if config.exists():174config = self.ropefolder.get_child('config.py')175execfile(config.real_path, run_globals)176else:177exec(self._default_config(), run_globals)178if 'set_prefs' in run_globals:179run_globals['set_prefs'](self.prefs)180for key, value in prefs.items():181self.prefs[key] = value182self._init_other_parts()183self._init_ropefolder()184if 'project_opened' in run_globals:185run_globals['project_opened'](self)186187def _default_config(self):188import rope.base.default_config189import inspect190return inspect.getsource(rope.base.default_config)191192def _init_other_parts(self):193# Forcing the creation of `self.pycore` to register observers194self.pycore195196def is_ignored(self, resource):197return self.ignored.does_match(resource)198199def sync(self):200"""Closes project open resources"""201self.close()202203def close(self):204"""Closes project open resources"""205self.data_files.write()206207def set(self, key, value):208"""Set the `key` preference to `value`"""209self.prefs.set(key, value)210211@property212def ropefolder(self):213if self._ropefolder_name is not None:214return self.get_folder(self._ropefolder_name)215216def validate(self, folder=None):217if folder is None:218folder = self.root219super(Project, self).validate(folder)220221root = property(lambda self: self.get_resource(''))222address = property(lambda self: self._address)223224225class NoProject(_Project):226"""A null object for holding out of project files.227228This class is singleton use `get_no_project` global function229"""230231def __init__(self):232fscommands = rope.base.fscommands.FileSystemCommands()233super(NoProject, self).__init__(fscommands)234235def _get_resource_path(self, name):236real_name = name.replace('/', os.path.sep)237return _realpath(real_name)238239def get_resource(self, name):240universal_name = _realpath(name).replace(os.path.sep, '/')241return super(NoProject, self).get_resource(universal_name)242243def get_files(self):244return []245246_no_project = None247248249def get_no_project():250if NoProject._no_project is None:251NoProject._no_project = NoProject()252return NoProject._no_project253254255class _FileListCacher(object):256257def __init__(self, project):258self.project = project259self.files = None260rawobserver = ResourceObserver(261self._changed, self._invalid, self._invalid,262self._invalid, self._invalid)263self.project.add_observer(rawobserver)264265def get_files(self):266if self.files is None:267self.files = set()268self._add_files(self.project.root)269return self.files270271def _add_files(self, folder):272for child in folder.get_children():273if child.is_folder():274self._add_files(child)275elif not self.project.is_ignored(child):276self.files.add(child)277278def _changed(self, resource):279if resource.is_folder():280self.files = None281282def _invalid(self, resource, new_resource=None):283self.files = None284285286class _DataFiles(object):287288def __init__(self, project):289self.project = project290self.hooks = []291292def read_data(self, name, compress=False, import_=False):293if self.project.ropefolder is None:294return None295compress = compress and self._can_compress()296opener = self._get_opener(compress)297file = self._get_file(name, compress)298if not compress and import_:299self._import_old_files(name)300if file.exists():301input = opener(file.real_path, 'rb')302try:303result = []304try:305while True:306result.append(pickle.load(input))307except EOFError:308pass309if len(result) == 1:310return result[0]311if len(result) > 1:312return result313finally:314input.close()315316def write_data(self, name, data, compress=False):317if self.project.ropefolder is not None:318compress = compress and self._can_compress()319file = self._get_file(name, compress)320opener = self._get_opener(compress)321output = opener(file.real_path, 'wb')322try:323pickle.dump(data, output, 2)324finally:325output.close()326327def add_write_hook(self, hook):328self.hooks.append(hook)329330def write(self):331for hook in self.hooks:332hook()333334def _can_compress(self):335try:336import gzip337return True338except ImportError:339return False340341def _import_old_files(self, name):342old = self._get_file(name + '.pickle', False)343new = self._get_file(name, False)344if old.exists() and not new.exists():345shutil.move(old.real_path, new.real_path)346347def _get_opener(self, compress):348if compress:349try:350import gzip351return gzip.open352except ImportError:353pass354return open355356def _get_file(self, name, compress):357path = self.project.ropefolder.path + '/' + name358if compress:359path += '.gz'360return self.project.get_file(path)361362363def _realpath(path):364"""Return the real path of `path`365366Is equivalent to ``realpath(abspath(expanduser(path)))``.367368"""369# there is a bug in cygwin for os.path.abspath() for abs paths370if sys.platform == 'cygwin':371if path[1:3] == ':\\':372return path373return os.path.abspath(os.path.expanduser(path))374return os.path.realpath(os.path.abspath(os.path.expanduser(path)))375376377