Path: blob/master/elisp/emacs-for-python/rope-dist/rope/contrib/generate.py
1428 views
import rope.base.evaluate1from rope.base import change, pyobjects, exceptions, pynames, worder, codeanalyze2from rope.refactor import sourceutils, importutils, functionutils, suites345def create_generate(kind, project, resource, offset):6"""A factory for creating `Generate` objects78`kind` can be 'variable', 'function', 'class', 'module' or9'package'.1011"""12generate = eval('Generate' + kind.title())13return generate(project, resource, offset)141516def create_module(project, name, sourcefolder=None):17"""Creates a module and returns a `rope.base.resources.File`"""18if sourcefolder is None:19sourcefolder = project.root20packages = name.split('.')21parent = sourcefolder22for package in packages[:-1]:23parent = parent.get_child(package)24return parent.create_file(packages[-1] + '.py')2526def create_package(project, name, sourcefolder=None):27"""Creates a package and returns a `rope.base.resources.Folder`"""28if sourcefolder is None:29sourcefolder = project.root30packages = name.split('.')31parent = sourcefolder32for package in packages[:-1]:33parent = parent.get_child(package)34made_packages = parent.create_folder(packages[-1])35made_packages.create_file('__init__.py')36return made_packages373839class _Generate(object):4041def __init__(self, project, resource, offset):42self.project = project43self.resource = resource44self.info = self._generate_info(project, resource, offset)45self.name = self.info.get_name()46self._check_exceptional_conditions()4748def _generate_info(self, project, resource, offset):49return _GenerationInfo(project.pycore, resource, offset)5051def _check_exceptional_conditions(self):52if self.info.element_already_exists():53raise exceptions.RefactoringError(54'Element <%s> already exists.' % self.name)55if not self.info.primary_is_found():56raise exceptions.RefactoringError(57'Cannot determine the scope <%s> should be defined in.' % self.name)5859def get_changes(self):60changes = change.ChangeSet('Generate %s <%s>' %61(self._get_element_kind(), self.name))62indents = self.info.get_scope_indents()63blanks = self.info.get_blank_lines()64base_definition = sourceutils.fix_indentation(self._get_element(), indents)65definition = '\n' * blanks[0] + base_definition + '\n' * blanks[1]6667resource = self.info.get_insertion_resource()68start, end = self.info.get_insertion_offsets()6970collector = codeanalyze.ChangeCollector(resource.read())71collector.add_change(start, end, definition)72changes.add_change(change.ChangeContents(73resource, collector.get_changed()))74return changes7576def get_location(self):77return (self.info.get_insertion_resource(),78self.info.get_insertion_lineno())7980def _get_element_kind(self):81raise NotImplementedError()8283def _get_element(self):84raise NotImplementedError()858687class GenerateFunction(_Generate):8889def _generate_info(self, project, resource, offset):90return _FunctionGenerationInfo(project.pycore, resource, offset)9192def _get_element(self):93decorator = ''94args = []95if self.info.is_static_method():96decorator = '@staticmethod\n'97if self.info.is_method() or self.info.is_constructor() or \98self.info.is_instance():99args.append('self')100args.extend(self.info.get_passed_args())101definition = '%sdef %s(%s):\n pass\n' % (decorator, self.name,102', '.join(args))103return definition104105def _get_element_kind(self):106return 'Function'107108109class GenerateVariable(_Generate):110111def _get_element(self):112return '%s = None\n' % self.name113114def _get_element_kind(self):115return 'Variable'116117118class GenerateClass(_Generate):119120def _get_element(self):121return 'class %s(object):\n pass\n' % self.name122123def _get_element_kind(self):124return 'Class'125126127class GenerateModule(_Generate):128129def get_changes(self):130package = self.info.get_package()131changes = change.ChangeSet('Generate Module <%s>' % self.name)132new_resource = self.project.get_file('%s/%s.py' % (package.path, self.name))133if new_resource.exists():134raise exceptions.RefactoringError(135'Module <%s> already exists' % new_resource.path)136changes.add_change(change.CreateResource(new_resource))137changes.add_change(_add_import_to_module(138self.project.pycore, self.resource, new_resource))139return changes140141def get_location(self):142package = self.info.get_package()143return (package.get_child('%s.py' % self.name) , 1)144145146class GeneratePackage(_Generate):147148def get_changes(self):149package = self.info.get_package()150changes = change.ChangeSet('Generate Package <%s>' % self.name)151new_resource = self.project.get_folder('%s/%s' % (package.path, self.name))152if new_resource.exists():153raise exceptions.RefactoringError(154'Package <%s> already exists' % new_resource.path)155changes.add_change(change.CreateResource(new_resource))156changes.add_change(_add_import_to_module(157self.project.pycore, self.resource, new_resource))158child = self.project.get_folder(package.path + '/' + self.name)159changes.add_change(change.CreateFile(child, '__init__.py'))160return changes161162def get_location(self):163package = self.info.get_package()164child = package.get_child(self.name)165return (child.get_child('__init__.py') , 1)166167168def _add_import_to_module(pycore, resource, imported):169pymodule = pycore.resource_to_pyobject(resource)170import_tools = importutils.ImportTools(pycore)171module_imports = import_tools.module_imports(pymodule)172module_name = pycore.modname(imported)173new_import = importutils.NormalImport(((module_name, None), ))174module_imports.add_import(new_import)175return change.ChangeContents(resource, module_imports.get_changed_source())176177178class _GenerationInfo(object):179180def __init__(self, pycore, resource, offset):181self.pycore = pycore182self.resource = resource183self.offset = offset184self.source_pymodule = self.pycore.resource_to_pyobject(resource)185finder = rope.base.evaluate.ScopeNameFinder(self.source_pymodule)186self.primary, self.pyname = finder.get_primary_and_pyname_at(offset)187self._init_fields()188189def _init_fields(self):190self.source_scope = self._get_source_scope()191self.goal_scope = self._get_goal_scope()192self.goal_pymodule = self._get_goal_module(self.goal_scope)193194def _get_goal_scope(self):195if self.primary is None:196return self._get_source_scope()197pyobject = self.primary.get_object()198if isinstance(pyobject, pyobjects.PyDefinedObject):199return pyobject.get_scope()200elif isinstance(pyobject.get_type(), pyobjects.PyClass):201return pyobject.get_type().get_scope()202203def _get_goal_module(self, scope):204if scope is None:205return206while scope.parent is not None:207scope = scope.parent208return scope.pyobject209210def _get_source_scope(self):211module_scope = self.source_pymodule.get_scope()212lineno = self.source_pymodule.lines.get_line_number(self.offset)213return module_scope.get_inner_scope_for_line(lineno)214215def get_insertion_lineno(self):216lines = self.goal_pymodule.lines217if self.goal_scope == self.source_scope:218line_finder = self.goal_pymodule.logical_lines219lineno = lines.get_line_number(self.offset)220lineno = line_finder.logical_line_in(lineno)[0]221root = suites.ast_suite_tree(self.goal_scope.pyobject.get_ast())222suite = root.find_suite(lineno)223indents = sourceutils.get_indents(lines, lineno)224while self.get_scope_indents() < indents:225lineno = suite.get_start()226indents = sourceutils.get_indents(lines, lineno)227suite = suite.parent228return lineno229else:230return min(self.goal_scope.get_end() + 1, lines.length())231232def get_insertion_resource(self):233return self.goal_pymodule.get_resource()234235def get_insertion_offsets(self):236if self.goal_scope.get_kind() == 'Class':237start, end = sourceutils.get_body_region(self.goal_scope.pyobject)238if self.goal_pymodule.source_code[start:end].strip() == 'pass':239return start, end240lines = self.goal_pymodule.lines241start = lines.get_line_start(self.get_insertion_lineno())242return (start, start)243244def get_scope_indents(self):245if self.goal_scope.get_kind() == 'Module':246return 0247return sourceutils.get_indents(self.goal_pymodule.lines,248self.goal_scope.get_start()) + 4249250def get_blank_lines(self):251if self.goal_scope.get_kind() == 'Module':252base_blanks = 2253if self.goal_pymodule.source_code.strip() == '':254base_blanks = 0255if self.goal_scope.get_kind() == 'Class':256base_blanks = 1257if self.goal_scope.get_kind() == 'Function':258base_blanks = 0259if self.goal_scope == self.source_scope:260return (0, base_blanks)261return (base_blanks, 0)262263def get_package(self):264primary = self.primary265if self.primary is None:266return self.pycore.get_source_folders()[0]267if isinstance(primary.get_object(), pyobjects.PyPackage):268return primary.get_object().get_resource()269raise exceptions.RefactoringError(270'A module/package can be only created in a package.')271272def primary_is_found(self):273return self.goal_scope is not None274275def element_already_exists(self):276if self.pyname is None or isinstance(self.pyname, pynames.UnboundName):277return False278return self.get_name() in self.goal_scope.get_defined_names()279280def get_name(self):281return worder.get_name_at(self.resource, self.offset)282283284class _FunctionGenerationInfo(_GenerationInfo):285286def _get_goal_scope(self):287if self.is_constructor():288return self.pyname.get_object().get_scope()289if self.is_instance():290return self.pyname.get_object().get_type().get_scope()291if self.primary is None:292return self._get_source_scope()293pyobject = self.primary.get_object()294if isinstance(pyobject, pyobjects.PyDefinedObject):295return pyobject.get_scope()296elif isinstance(pyobject.get_type(), pyobjects.PyClass):297return pyobject.get_type().get_scope()298299def element_already_exists(self):300if self.pyname is None or isinstance(self.pyname, pynames.UnboundName):301return False302return self.get_name() in self.goal_scope.get_defined_names()303304def is_static_method(self):305return self.primary is not None and \306isinstance(self.primary.get_object(), pyobjects.PyClass)307308def is_method(self):309return self.primary is not None and \310isinstance(self.primary.get_object().get_type(), pyobjects.PyClass)311312def is_constructor(self):313return self.pyname is not None and \314isinstance(self.pyname.get_object(), pyobjects.PyClass)315316def is_instance(self):317if self.pyname is None:318return False319pyobject = self.pyname.get_object()320return isinstance(pyobject.get_type(), pyobjects.PyClass)321322def get_name(self):323if self.is_constructor():324return '__init__'325if self.is_instance():326return '__call__'327return worder.get_name_at(self.resource, self.offset)328329def get_passed_args(self):330result = []331source = self.source_pymodule.source_code332finder = worder.Worder(source)333if finder.is_a_function_being_called(self.offset):334start, end = finder.get_primary_range(self.offset)335parens_start, parens_end = finder.get_word_parens_range(end - 1)336call = source[start:parens_end]337parser = functionutils._FunctionParser(call, False)338args, keywords = parser.get_parameters()339for arg in args:340if self._is_id(arg):341result.append(arg)342else:343result.append('arg%d' % len(result))344for name, value in keywords:345result.append(name)346return result347348def _is_id(self, arg):349def id_or_underline(c):350return c.isalpha() or c == '_'351for c in arg:352if not id_or_underline(c) and not c.isdigit():353return False354return id_or_underline(arg[0])355356357