def extract(script, new_name): """ The `args` / `kwargs` params are the same as in `api.Script`. :param operation: The refactoring operation to execute. :type operation: str :type source: str :return: list of changed lines/changed files """ new_lines = split_lines(python_bytes_to_unicode(script.source)) old_lines = new_lines[:] user_stmt = script._parser.user_stmt() # TODO care for multiline extracts dct = {} if user_stmt: pos = script._pos line_index = pos[0] - 1 arr, index = helpers.array_for_pos(user_stmt, pos) if arr is not None: start_pos = arr[index].start_pos end_pos = arr[index].end_pos # take full line if the start line is different from end line e = end_pos[1] if end_pos[0] == start_pos[0] else None start_line = new_lines[start_pos[0] - 1] text = start_line[start_pos[1]:e] for l in range(start_pos[0], end_pos[0] - 1): text += '\n' + l if e is None: end_line = new_lines[end_pos[0] - 1] text += '\n' + end_line[:end_pos[1]] # remove code from new lines t = text.lstrip() del_start = start_pos[1] + len(text) - len(t) text = t.rstrip() del_end = len(t) - len(text) if e is None: new_lines[end_pos[0] - 1] = end_line[end_pos[1] - del_end:] e = len(start_line) else: e = e - del_end start_line = start_line[:del_start] + new_name + start_line[e:] new_lines[start_pos[0] - 1] = start_line new_lines[start_pos[0]:end_pos[0] - 1] = [] # add parentheses in multiline case open_brackets = ['(', '[', '{'] close_brackets = [')', ']', '}'] if '\n' in text and not (text[0] in open_brackets and text[-1] == close_brackets[open_brackets.index(text[0])]): text = '(%s)' % text # add new line before statement indent = user_stmt.start_pos[1] new = "%s%s = %s" % (' ' * indent, new_name, text) new_lines.insert(line_index, new) dct[script.path] = script.path, old_lines, new_lines return Refactoring(dct)
def _py__path__(self): search_path = self.evaluator.get_sys_path() init_path = self.py__file__() if os.path.basename(init_path) == '__init__.py': with open(init_path, 'rb') as f: content = python_bytes_to_unicode(f.read(), errors='replace') # these are strings that need to be used for namespace packages, # the first one is ``pkgutil``, the second ``pkg_resources``. options = ('declare_namespace(__name__)', 'extend_path(__path__') if options[0] in content or options[1] in content: # It is a namespace, now try to find the rest of the # modules on sys_path or whatever the search_path is. paths = set() for s in search_path: other = os.path.join(s, self.name.string_name) if os.path.isdir(other): paths.add(other) if paths: return list(paths) # TODO I'm not sure if this is how nested namespace # packages work. The tests are not really good enough to # show that. # Default to this. return [self._get_init_directory()]
def _rename(names, replace_str): """ For both rename and inline. """ order = sorted(names, key=lambda x: (x.module_path, x.line, x.column), reverse=True) def process(path, old_lines, new_lines): if new_lines is not None: # goto next file, save last dct[path] = path, old_lines, new_lines dct = {} current_path = object() new_lines = old_lines = None for name in order: if name.in_builtin_module(): continue if current_path != name.module_path: current_path = name.module_path process(current_path, old_lines, new_lines) if current_path is not None: # None means take the source that is a normal param. with open(current_path) as f: source = f.read() new_lines = split_lines(python_bytes_to_unicode(source)) old_lines = new_lines[:] nr, indent = name.line, name.column line = new_lines[nr - 1] new_lines[nr - 1] = line[:indent] + replace_str + \ line[indent + len(name.name):] process(current_path, old_lines, new_lines) return dct
def __init__(self, source=None, line=None, column=None, path=None, encoding='utf-8', sys_path=None): self._orig_path = path # An empty path (also empty string) should always result in no path. self.path = os.path.abspath(path) if path else None if source is None: # TODO add a better warning than the traceback! with open(path, 'rb') as f: source = f.read() # TODO do we really want that? self._source = python_bytes_to_unicode(source, encoding, errors='replace') self._code_lines = split_lines(self._source) line = max(len(self._code_lines), 1) if line is None else line if not (0 < line <= len(self._code_lines)): raise ValueError('`line` parameter is not in a valid range.') line_len = len(self._code_lines[line - 1]) column = line_len if column is None else column if not (0 <= column <= line_len): raise ValueError('`column` parameter is not in a valid range.') self._pos = line, column self._path = path cache.clear_time_caches() debug.reset_time() # Load the Python grammar of the current interpreter. self._grammar = parso.load_grammar() project = Project(sys_path=sys_path) self._evaluator = Evaluator(self._grammar, project) project.add_script_path(self.path) debug.speed('init')
def parse_and_get_code(self, code=None, path=None, **kwargs): if self.allow_different_encoding: if code is None: with open(path, 'rb') as f: code = f.read() code = python_bytes_to_unicode(code, errors='replace') return self.grammar.parse(code=code, path=path, **kwargs), code
def check_fs(path): with open(path, 'rb') as f: code = python_bytes_to_unicode(f.read(), errors='replace') if name in code: module = _load_module(evaluator, path, code) module_name = sys_path.dotted_path_in_sys_path(evaluator.sys_path, path) if module_name is not None: add_module(evaluator, module_name, module) return module
def check_fs(path, base_names): try: f = open(path, 'rb') except FileNotFoundError: return None with f: code = python_bytes_to_unicode(f.read(), errors='replace') if name not in code: return None return _load_module_from_path(evaluator, path, base_names, code)
def check_fs(path): with open(path, 'rb') as f: code = python_bytes_to_unicode(f.read(), errors='replace') if name in code: module = _load_module(evaluator, path, code) module_name = sys_path.dotted_path_in_sys_path( evaluator.project.sys_path, path) if module_name is not None: add_module(evaluator, module_name, module) return module
def check_fs(path): with open(path, 'rb') as f: code = python_bytes_to_unicode(f.read(), errors='replace') if name in code: e_sys_path = evaluator.get_sys_path() module_name = sys_path.dotted_path_in_sys_path(e_sys_path, path) module = _load_module( evaluator, path, code, sys_path=e_sys_path, module_name=module_name ) return module
def parse_and_get_code(self, code=None, path=None, encoding='utf-8', use_latest_grammar=False, file_io=None, **kwargs): if self.allow_different_encoding: if code is None: if file_io is None: file_io = FileIO(path) code = file_io.read() code = python_bytes_to_unicode(code, encoding=encoding, errors='replace') grammar = self.latest_grammar if use_latest_grammar else self.grammar return grammar.parse(code=code, path=path, file_io=file_io, **kwargs), code
def check_fs(file_io, base_names): try: code = file_io.read() except FileNotFoundError: return None code = python_bytes_to_unicode(code, errors='replace') if name not in code: return None new_file_io = KnownContentFileIO(file_io.path, code) m = load_module_from_path(inference_state, new_file_io, base_names) if isinstance(m, compiled.CompiledObject): return None return m.as_context()
def _check_fs(inference_state, file_io, regex): try: code = file_io.read() except FileNotFoundError: return None code = python_bytes_to_unicode(code, errors='replace') if not regex.search(code): return None new_file_io = KnownContentFileIO(file_io.path, code) m = load_module_from_path(inference_state, new_file_io) if m.is_compiled(): return None return m.as_context()
def parse_and_get_code(self, code=None, path=None, use_latest_grammar=False, file_io=None, **kwargs): if path is not None: path = str(path) if code is None: if file_io is None: file_io = FileIO(path) code = file_io.read() # We cannot just use parso, because it doesn't use errors='replace'. code = parso.python_bytes_to_unicode(code, encoding='utf-8', errors='replace') if len(code) > settings._cropped_file_size: code = code[:settings._cropped_file_size] grammar = self.latest_grammar if use_latest_grammar else self.grammar return grammar.parse(code=code, path=path, file_io=file_io, **kwargs), code
def check_fs(path): try: f = open(path, 'rb') except FileNotFoundError: return with f: code = python_bytes_to_unicode(f.read(), errors='replace') if name in code: e_sys_path = evaluator.get_sys_path() import_names = sys_path.transform_path_to_dotted(e_sys_path, path) module = _load_module( evaluator, path, code, sys_path=e_sys_path, import_names=import_names, ) return module
def inline(script): """ :type script: api.Script """ new_lines = split_lines(python_bytes_to_unicode(script.source)) dct = {} definitions = script.goto_assignments() assert len(definitions) == 1 stmt = definitions[0]._definition usages = script.usages() inlines = [ r for r in usages if not stmt.start_pos <= (r.line, r.column) <= stmt.end_pos ] inlines = sorted(inlines, key=lambda x: (x.module_path, x.line, x.column), reverse=True) expression_list = stmt.expression_list() # don't allow multi-line refactorings for now. assert stmt.start_pos[0] == stmt.end_pos[0] index = stmt.start_pos[0] - 1 line = new_lines[index] replace_str = line[expression_list[0].start_pos[1]:stmt.end_pos[1] + 1] replace_str = replace_str.strip() # tuples need parentheses if expression_list and isinstance(expression_list[0], pr.Array): arr = expression_list[0] if replace_str[0] not in ['(', '[', '{'] and len(arr) > 1: replace_str = '(%s)' % replace_str # if it's the only assignment, remove the statement if len(stmt.get_defined_names()) == 1: line = line[:stmt.start_pos[1]] + line[stmt.end_pos[1]:] dct = _rename(inlines, replace_str) # remove the empty line new_lines = dct[script.path][2] if line.strip(): new_lines[index] = line else: new_lines.pop(index) return Refactoring(dct)
def inline(script): """ :type script: api.Script """ new_lines = split_lines(python_bytes_to_unicode(script.source)) dct = {} definitions = script.goto_assignments() with common.ignored(AssertionError): assert len(definitions) == 1 stmt = definitions[0]._definition usages = script.usages() inlines = [r for r in usages if not stmt.start_pos <= (r.line, r.column) <= stmt.end_pos] inlines = sorted(inlines, key=lambda x: (x.module_path, x.line, x.column), reverse=True) expression_list = stmt.expression_list() # don't allow multiline refactorings for now. assert stmt.start_pos[0] == stmt.end_pos[0] index = stmt.start_pos[0] - 1 line = new_lines[index] replace_str = line[expression_list[0].start_pos[1]:stmt.end_pos[1] + 1] replace_str = replace_str.strip() # tuples need parentheses if expression_list and isinstance(expression_list[0], pr.Array): arr = expression_list[0] if replace_str[0] not in ['(', '[', '{'] and len(arr) > 1: replace_str = '(%s)' % replace_str # if it's the only assignment, remove the statement if len(stmt.get_defined_names()) == 1: line = line[:stmt.start_pos[1]] + line[stmt.end_pos[1]:] dct = _rename(inlines, replace_str) # remove the empty line new_lines = dct[script.path][2] if line.strip(): new_lines[index] = line else: new_lines.pop(index) return Refactoring(dct)
def __init__(self, source=None, line=None, column=None, path=None, encoding='utf-8', sys_path=None): self._orig_path = path # An empty path (also empty string) should always result in no path. self.path = os.path.abspath(path) if path else None if source is None: # TODO add a better warning than the traceback! with open(path, 'rb') as f: source = f.read() # TODO do we really want that? self._source = python_bytes_to_unicode(source, encoding, errors='replace') self._code_lines = split_lines(self._source) line = max(len(self._code_lines), 1) if line is None else line if not (0 < line <= len(self._code_lines)): raise ValueError('`line` parameter is not in a valid range.') line_len = len(self._code_lines[line - 1]) column = line_len if column is None else column if not (0 <= column <= line_len): raise ValueError('`column` parameter is not in a valid range.') self._pos = line, column self._path = path cache.clear_time_caches() debug.reset_time() # Load the Python grammar of the current interpreter. self._grammar = parso.load_grammar() if sys_path is None: venv = os.getenv('VIRTUAL_ENV') if venv: sys_path = list(get_venv_path(venv)) self._evaluator = Evaluator(self._grammar, sys_path=sys_path) debug.speed('init')