use weakrefs for parents now: functions.py

This commit is contained in:
David Halter
2012-08-12 01:23:57 +02:00
parent 1571931088
commit ed07e91d88
2 changed files with 64 additions and 39 deletions

View File

@@ -1,4 +1,5 @@
import re import re
import weakref
import parsing import parsing
import dynamic # must be before evaluate, because it needs to be loaded first. import dynamic # must be before evaluate, because it needs to be loaded first.
@@ -28,17 +29,17 @@ class Completion(object):
@property @property
def description(self): def description(self):
return str(self.name.parent) return str(self.name.parent())
@property @property
def help(self): def help(self):
try: try:
return str(self.name.parent.docstr) return str(self.name.parent().docstr)
except AttributeError: except AttributeError:
return '' return ''
def get_type(self): def get_type(self):
return type(self.name.parent) return type(self.name.parent())
def get_vim_type(self): def get_vim_type(self):
""" """
@@ -94,8 +95,8 @@ class Definition(object):
def module_path(self): def module_path(self):
par = self.definition par = self.definition
while True: while True:
if par.parent is not None: if par.parent() is not None:
par = par.parent par = par.parent()
else: else:
break break
@@ -118,7 +119,7 @@ class Definition(object):
if isinstance(d, evaluate.InstanceElement): if isinstance(d, evaluate.InstanceElement):
d = d.var d = d.var
if isinstance(d, evaluate.parsing.Name): if isinstance(d, evaluate.parsing.Name):
d = d.parent d = d.parent()
if isinstance(d, (evaluate.Class, evaluate.Instance)): if isinstance(d, (evaluate.Class, evaluate.Instance)):
d = 'class ' + str(d.name) d = 'class ' + str(d.name)
@@ -223,7 +224,7 @@ def prepare_goto(source, position, source_path, module, goto_path,
raise NotFoundError() raise NotFoundError()
else: else:
stmt.start_pos = position stmt.start_pos = position
stmt.parent = scope stmt.parent = weakref.ref(scope)
scopes = evaluate.follow_statement(stmt) scopes = evaluate.follow_statement(stmt)
return scopes return scopes
@@ -284,7 +285,7 @@ def goto(source, line, column, source_path):
""" """
new_names = [] new_names = []
for n in names: for n in names:
par = n.parent par = n.parent()
# This is a special case: If the Import is "virtual" (which # This is a special case: If the Import is "virtual" (which
# means the position is not defined), follow those modules. # means the position is not defined), follow those modules.
if isinstance(par, parsing.Import) and not par.start_pos[0]: if isinstance(par, parsing.Import) and not par.start_pos[0]:
@@ -321,3 +322,14 @@ def set_debug_function(func_cb):
def _clear_caches(): def _clear_caches():
evaluate.clear_caches() evaluate.clear_caches()
import gc
#gc.set_debug(gc.DEBUG_STATS | gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS)
#gc.collect()
count = 0
for o in gc.get_objects():
if isinstance(o, parsing.Module):
pass
count += 1
#print o
print count
#exit()

View File

@@ -34,6 +34,7 @@ from _compatibility import (next, literal_eval, tokenize_func, BytesIO,
import tokenize import tokenize
import re import re
import keyword import keyword
import weakref
import debug import debug
@@ -73,13 +74,13 @@ class Simple(Base):
def __init__(self, start_pos, end_pos=(None, None)): def __init__(self, start_pos, end_pos=(None, None)):
self.start_pos = start_pos self.start_pos = start_pos
self.end_pos = end_pos self.end_pos = end_pos
self.parent = None self.parent = lambda: None
def get_parent_until(self, *classes): def get_parent_until(self, *classes):
""" Takes always the parent, until one class (not a Class) """ """ Takes always the parent, until one class (not a Class) """
scope = self scope = self
while not (scope.parent is None or scope.__class__ in classes): while not (scope.parent() is None or scope.__class__ in classes):
scope = scope.parent scope = scope.parent()
return scope return scope
def __repr__(self): def __repr__(self):
@@ -109,8 +110,8 @@ class Scope(Simple):
self.docstr = docstr self.docstr = docstr
def add_scope(self, sub, decorators): def add_scope(self, sub, decorators):
# print 'push scope: [%s@%s]' % sub.start_pos print 'push scope @%s,%s' % sub.start_pos
sub.parent = self sub.parent = weakref.ref(self)
sub.decorators = decorators sub.decorators = decorators
for d in decorators: for d in decorators:
# the parent is the same, because the decorator has not the scope # the parent is the same, because the decorator has not the scope
@@ -124,7 +125,7 @@ class Scope(Simple):
Used to add a Statement or a Scope. Used to add a Statement or a Scope.
A statement would be a normal command (Statement) or a Scope (Flow). A statement would be a normal command (Statement) or a Scope (Flow).
""" """
stmt.parent = self stmt.parent = weakref.ref(self)
self.statements.append(stmt) self.statements.append(stmt)
return stmt return stmt
@@ -154,7 +155,7 @@ class Scope(Simple):
def add_import(self, imp): def add_import(self, imp):
self.imports.append(imp) self.imports.append(imp)
imp.parent = self imp.parent = weakref.ref(self)
def get_imports(self): def get_imports(self):
""" Gets also the imports within flow statements """ """ Gets also the imports within flow statements """
@@ -291,10 +292,10 @@ class Class(Scope):
def __init__(self, name, supers, start_pos, docstr=''): def __init__(self, name, supers, start_pos, docstr=''):
super(Class, self).__init__(start_pos, docstr) super(Class, self).__init__(start_pos, docstr)
self.name = name self.name = name
name.parent = self name.parent = weakref.ref(self)
self.supers = supers self.supers = supers
for s in self.supers: for s in self.supers:
s.parent = self s.parent = weakref.ref(self)
self.decorators = [] self.decorators = []
def get_code(self, first_indent=False, indention=" "): def get_code(self, first_indent=False, indention=" "):
@@ -326,10 +327,10 @@ class Function(Scope):
def __init__(self, name, params, start_pos, docstr=''): def __init__(self, name, params, start_pos, docstr=''):
Scope.__init__(self, start_pos, docstr) Scope.__init__(self, start_pos, docstr)
self.name = name self.name = name
name.parent = self name.parent = weakref.ref(self)
self.params = params self.params = params
for p in params: for p in params:
p.parent = self p.parent = weakref.ref(self)
self.decorators = [] self.decorators = []
self.returns = [] self.returns = []
self.is_generator = False self.is_generator = False
@@ -378,30 +379,37 @@ class Flow(Scope):
:type set_vars: list :type set_vars: list
""" """
def __init__(self, command, inits, start_pos, set_vars=None): def __init__(self, command, inits, start_pos, set_vars=None):
self._parent = None
self.next = None
super(Flow, self).__init__(start_pos, '') super(Flow, self).__init__(start_pos, '')
self.next = None
#self.top_flow = weakref.ref(self)
self.command = command self.command = command
# These have to be statements, because of with, which takes multiple. # These have to be statements, because of with, which takes multiple.
self.inits = inits self.inits = inits
for s in inits: for s in inits:
s.parent = self s.parent = weakref.ref(self)
if set_vars == None: if set_vars == None:
self.set_vars = [] self.set_vars = []
else: else:
self.set_vars = set_vars self.set_vars = set_vars
for s in self.set_vars: for s in self.set_vars:
s.parent = self s.parent = weakref.ref(self)
@property """
def parent(self): def parent(self):
return self._parent if self._parent is None:
return self.top_flow().parent()
else:
return self._parent()
# TODO REMOVE
"""
@parent.setter def set_parent(self, value):
def parent(self, value): """
self._parent = value Normally this would be a setter, but since parents are normally
if self.next: weakrefs (and therefore require execution),
self.next.parent = value I use a java like setter here.
"""
self._parent = weakref.ref(value)
def get_code(self, first_indent=False, indention=" "): def get_code(self, first_indent=False, indention=" "):
if self.set_vars: if self.set_vars:
@@ -451,7 +459,9 @@ class Flow(Scope):
return self.next.set_next(next) return self.next.set_next(next)
else: else:
self.next = next self.next = next
next.parent = self.parent self.next.parent = self.parent
print 'n', self.next
#next.top_flow = self.top_flow
return next return next
@@ -491,15 +501,15 @@ class Import(Simple):
self.namespace = namespace self.namespace = namespace
if namespace: if namespace:
namespace.parent = self namespace.parent = weakref.ref(self)
self.alias = alias self.alias = alias
if alias: if alias:
alias.parent = self alias.parent = weakref.ref(self)
self.from_ns = from_ns self.from_ns = from_ns
if from_ns: if from_ns:
from_ns.parent = self from_ns.parent = weakref.ref(self)
self.star = star self.star = star
self.relative_count = relative_count self.relative_count = relative_count
@@ -561,7 +571,7 @@ class Statement(Simple):
self.used_vars = used_vars self.used_vars = used_vars
self.token_list = token_list self.token_list = token_list
for s in set_vars + used_funcs + used_vars: for s in set_vars + used_funcs + used_vars:
s.parent = self s.parent = weakref.ref(self)
# cache # cache
self._assignment_calls = None self._assignment_calls = None
@@ -938,7 +948,8 @@ class Name(Simple):
def __init__(self, names, start_pos, end_pos, parent=None): def __init__(self, names, start_pos, end_pos, parent=None):
super(Name, self).__init__(start_pos, end_pos) super(Name, self).__init__(start_pos, end_pos)
self.names = tuple(NamePart(n) for n in names) self.names = tuple(NamePart(n) for n in names)
self.parent = parent if parent is not None:
self.parent = weakref.ref(parent)
def get_code(self): def get_code(self):
""" Returns the names in a full string format """ """ Returns the names in a full string format """
@@ -1326,7 +1337,7 @@ class PyFuzzyParser(object):
""" """
buf = BytesIO(self.code) buf = BytesIO(self.code)
self.gen = tokenize_func(buf.readline) self.gen = tokenize_func(buf.readline)
self.currentscope = self.scope self.currentscope = self.scope # TODO remove?
extended_flow = ['else', 'elif', 'except', 'finally'] extended_flow = ['else', 'elif', 'except', 'finally']
statement_toks = ['{', '[', '(', '`'] statement_toks = ['{', '[', '(', '`']
@@ -1338,6 +1349,8 @@ class PyFuzzyParser(object):
token_type, tok = self.next() token_type, tok = self.next()
#debug.dbg('main: tok=[%s] type=[%s] indent=[%s]'\ #debug.dbg('main: tok=[%s] type=[%s] indent=[%s]'\
# % (tok, token_type, start_position[0])) # % (tok, token_type, start_position[0]))
print('main: tok=[%s] type=[%s] indent=[%s]'
% (tok, tokenize.tok_name[token_type], self.start_pos[0]))
while token_type == tokenize.DEDENT \ while token_type == tokenize.DEDENT \
and self.scope != self.module: and self.scope != self.module:
@@ -1345,7 +1358,7 @@ class PyFuzzyParser(object):
token_type, tok = self.next() token_type, tok = self.next()
if self.start_pos[1] <= self.scope.start_pos[1]: if self.start_pos[1] <= self.scope.start_pos[1]:
self.scope.end_pos = self.start_pos self.scope.end_pos = self.start_pos
self.scope = self.scope.parent self.scope = self.scope.parent()
# check again for unindented stuff. this is true for syntax # check again for unindented stuff. this is true for syntax
# errors. only check for names, because thats relevant here. If # errors. only check for names, because thats relevant here. If
@@ -1355,7 +1368,7 @@ class PyFuzzyParser(object):
and self.scope != self.module: and self.scope != self.module:
debug.dbg('syntax: dedent @%s - %s<=%s', self.start_pos) debug.dbg('syntax: dedent @%s - %s<=%s', self.start_pos)
self.scope.end_pos = self.start_pos self.scope.end_pos = self.start_pos
self.scope = self.scope.parent self.scope = self.scope.parent()
first_pos = self.start_pos first_pos = self.start_pos
if tok == 'def': if tok == 'def':