From: Mike Bayer Date: Fri, 7 Nov 2008 16:41:54 +0000 (+0000) Subject: the @memoized_property fairy pays a visit X-Git-Tag: rel_0_5rc3~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=abf9bef1a9f548a373f05b8a328d815dd28dda30;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git the @memoized_property fairy pays a visit --- diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 48090e8b7c..74f3e6093f 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -773,7 +773,6 @@ class ForeignKey(SchemaItem): """ self._colspec = column - self._column = None self.constraint = constraint self.use_alter = use_alter self.name = name @@ -812,67 +811,65 @@ class ForeignKey(SchemaItem): return table.corresponding_column(self.column) + @util.memoized_property def column(self): # ForeignKey inits its remote column as late as possible, so tables # can be defined without dependencies - if self._column is None: - if isinstance(self._colspec, basestring): - # locate the parent table this foreign key is attached to. we - # use the "original" column which our parent column represents - # (its a list of columns/other ColumnElements if the parent - # table is a UNION) - for c in self.parent.base_columns: - if isinstance(c, Column): - parenttable = c.table - break - else: - raise exc.ArgumentError( - "Parent column '%s' does not descend from a " - "table-attached Column" % str(self.parent)) - m = re.match(r"^(.+?)(?:\.(.+?))?(?:\.(.+?))?$", self._colspec, - re.UNICODE) - if m is None: - raise exc.ArgumentError( - "Invalid foreign key column specification: %s" % - self._colspec) - if m.group(3) is None: - (tname, colname) = m.group(1, 2) - schema = None - else: - (schema, tname, colname) = m.group(1, 2, 3) - if _get_table_key(tname, schema) not in parenttable.metadata: - raise exc.NoReferencedTableError( - "Could not find table '%s' with which to generate a " - "foreign key" % tname) - table = Table(tname, parenttable.metadata, - mustexist=True, schema=schema) - try: - if colname is None: - # colname is None in the case that ForeignKey argument - # was specified as table name only, in which case we - # match the column name to the same column on the - # parent. - key = self.parent - self._column = table.c[self.parent.key] - else: - self._column = table.c[colname] - except KeyError, e: - raise exc.NoReferencedColumnError( - "Could not create ForeignKey '%s' on table '%s': " - "table '%s' has no column named '%s'" % ( - self._colspec, parenttable.name, table.name, str(e))) - - elif hasattr(self._colspec, '__clause_element__'): - self._column = self._colspec.__clause_element__() + if isinstance(self._colspec, basestring): + # locate the parent table this foreign key is attached to. we + # use the "original" column which our parent column represents + # (its a list of columns/other ColumnElements if the parent + # table is a UNION) + for c in self.parent.base_columns: + if isinstance(c, Column): + parenttable = c.table + break else: - self._column = self._colspec + raise exc.ArgumentError( + "Parent column '%s' does not descend from a " + "table-attached Column" % str(self.parent)) + m = re.match(r"^(.+?)(?:\.(.+?))?(?:\.(.+?))?$", self._colspec, + re.UNICODE) + if m is None: + raise exc.ArgumentError( + "Invalid foreign key column specification: %s" % + self._colspec) + if m.group(3) is None: + (tname, colname) = m.group(1, 2) + schema = None + else: + (schema, tname, colname) = m.group(1, 2, 3) + if _get_table_key(tname, schema) not in parenttable.metadata: + raise exc.NoReferencedTableError( + "Could not find table '%s' with which to generate a " + "foreign key" % tname) + table = Table(tname, parenttable.metadata, + mustexist=True, schema=schema) + try: + if colname is None: + # colname is None in the case that ForeignKey argument + # was specified as table name only, in which case we + # match the column name to the same column on the + # parent. + key = self.parent + _column = table.c[self.parent.key] + else: + _column = table.c[colname] + except KeyError, e: + raise exc.NoReferencedColumnError( + "Could not create ForeignKey '%s' on table '%s': " + "table '%s' has no column named '%s'" % ( + self._colspec, parenttable.name, table.name, str(e))) + + elif hasattr(self._colspec, '__clause_element__'): + _column = self._colspec.__clause_element__() + else: + _column = self._colspec # propagate TypeEngine to parent if it didn't have one if isinstance(self.parent.type, types.NullType): - self.parent.type = self._column.type - return self._column - - column = property(column) + self.parent.type = _column.type + return _column def _set_parent(self, column): self.parent = column diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py index 5206dc5fa8..92659494f8 100644 --- a/lib/sqlalchemy/sql/expression.py +++ b/lib/sqlalchemy/sql/expression.py @@ -978,6 +978,7 @@ class ClauseElement(Visitable): """ c = self.__class__.__new__(self.__class__) c.__dict__ = self.__dict__.copy() + c.__dict__.pop('_cloned_set', None) # this is a marker that helps to "equate" clauses to each other # when a Select returns its list of FROM clauses. the cloning @@ -988,7 +989,7 @@ class ClauseElement(Visitable): return c - @property + @util.memoized_property def _cloned_set(self): """Return the set consisting all cloned anscestors of this ClauseElement. @@ -997,11 +998,13 @@ class ClauseElement(Visitable): of transformative operations. """ + s = set() f = self while f is not None: - yield f + s.add(f) f = getattr(f, '_is_clone_of', None) - + return s + def __getstate__(self): d = self.__dict__.copy() d.pop('_is_clone_of', None) @@ -1562,23 +1565,19 @@ class ColumnElement(ClauseElement, _CompareMixin): def _select_iterable(self): return (self, ) - @property + @util.memoized_property def base_columns(self): - if not hasattr(self, '_base_columns'): - self._base_columns = set(c for c in self.proxy_set + return set(c for c in self.proxy_set if not hasattr(c, 'proxies')) - return self._base_columns - @property + @util.memoized_property def proxy_set(self): - if not hasattr(self, '_proxy_set'): - s = set([self]) - if hasattr(self, 'proxies'): - for c in self.proxies: - s.update(c.proxy_set) - self._proxy_set = s - return self._proxy_set - + s = set([self]) + if hasattr(self, 'proxies'): + for c in self.proxies: + s.update(c.proxy_set) + return s + def shares_lineage(self, othercolumn): """Return True if the given ``ColumnElement`` has a common ancestor to this ``ColumnElement``.""" @@ -1773,7 +1772,7 @@ class FromClause(Selectable): An example would be an Alias of a Table is derived from that Table. """ - return fromclause in set(self._cloned_set) + return fromclause in self._cloned_set def replace_selectable(self, old, alias): """replace all occurences of FromClause 'old' with the given Alias object, returning a copy of this ``FromClause``.""" @@ -2544,7 +2543,7 @@ class Alias(FromClause): return self.name.encode('ascii', 'backslashreplace') def is_derived_from(self, fromclause): - if fromclause in set(self._cloned_set): + if fromclause in self._cloned_set: return True return self.element.is_derived_from(fromclause) @@ -2588,10 +2587,7 @@ class _Grouping(ColumnElement): @property def _label(self): - try: - return self.element._label - except AttributeError: - return self.anon_label + return getattr(self.element, '_label', None) or self.anon_label def _copy_internals(self, clone=_clone): self.element = clone(self.element) @@ -3192,7 +3188,7 @@ class Select(_SelectBaseMixin, FromClause): return itertools.chain(*[c._select_iterable for c in self._raw_columns]) def is_derived_from(self, fromclause): - if self in set(fromclause._cloned_set): + if self in fromclause._cloned_set: return True for f in self.locate_all_froms():