Exemple #1
0
def test_obj_info_primary_vars(cls_info, obj_info):
    assert isinstance(obj_info.primary_vars, tuple)
    for column, variable in iter_zip(cls_info.primary_key,
                                     obj_info.primary_vars):
        assert obj_info.variables.get(column) == variable

    assert len(obj_info.primary_vars) == len(cls_info.primary_key)
Exemple #2
0
    def __init__(self, local_key, remote_key, many, on_remote):
        assert type(local_key) is tuple and type(remote_key) is tuple

        self.local_key = local_key
        self.remote_key = remote_key

        self.local_cls = getattr(self.local_key[0], "cls", None)
        self.remote_cls = self.remote_key[0].cls
        self.remote_key_is_primary = False

        primary_key = get_cls_info(self.remote_cls).primary_key
        if len(primary_key) == len(self.remote_key):
            for column1, column2 in iter_zip(self.remote_key, primary_key):
                if column1.name != column2.name:
                    break
            else:
                self.remote_key_is_primary = True

        self.many = many
        self.on_remote = on_remote

        # XXX These should probably be weak dictionaries.
        self._local_columns = {}
        self._remote_columns = {}

        self._l_to_r = {}
        self._r_to_l = {}
Exemple #3
0
    def get(self, name, namespace=None):
        """Translate a property name path to the actual property.

        This method accepts a property name like C{"id"} or C{"Class.id"}
        or C{"module.path.Class.id"}, and tries to find a unique
        class/property with the given name.

        When the C{namespace} argument is given, the registry will be
        able to disambiguate names by choosing the one that is closer
        to the given namespace.  For instance C{get("Class.id", "a.b.c")}
        will choose C{a.Class.id} rather than C{d.Class.id}.
        """
        key = ".".join(reversed(name.split("."))) + "."
        i = bisect_left(self._properties, (key, ))
        l = len(self._properties)
        best_props = []
        if namespace is None:
            while i < l and self._properties[i][0].startswith(key):
                path, prop_ref = self._properties[i]
                prop = prop_ref()
                if prop is not None:
                    best_props.append((path, prop))
                i += 1
        else:
            namespace_parts = ("." + namespace).split(".")
            best_path_info = (0, sys.maxsize)
            while i < l and self._properties[i][0].startswith(key):
                path, prop_ref = self._properties[i]
                prop = prop_ref()
                if prop is None:
                    i += 1
                    continue
                path_parts = path.split(".")
                path_parts.reverse()
                common_prefix = 0
                for part, ns_part in iter_zip(path_parts, namespace_parts):
                    if part == ns_part:
                        common_prefix += 1
                    else:
                        break
                path_info = (-common_prefix, len(path_parts) - common_prefix)
                if path_info < best_path_info:
                    best_path_info = path_info
                    best_props = [(path, prop)]
                elif path_info == best_path_info:
                    best_props.append((path, prop))
                i += 1
        if not best_props:
            raise PropertyPathError("Path '%s' matches no known property." %
                                    name)
        elif len(best_props) > 1:
            paths = [
                ".".join(reversed(path.split(".")[:-1]))
                for path, prop in best_props
            ]
            raise PropertyPathError("Path '%s' matches multiple "
                                    "properties: %s" %
                                    (name, ", ".join(paths)))
        return best_props[0][1]
Exemple #4
0
 def _get_local_column(self, local_cls, remote_column):
     try:
         return self._r_to_l[local_cls].get(remote_column)
     except KeyError:
         map = {}
         for local_prop, _remote_column in iter_zip(self.local_key,
                                                    self.remote_key):
             map[_remote_column] = local_prop.__get__(None, local_cls)
         return self._r_to_l.setdefault(local_cls, map).get(remote_column)
Exemple #5
0
 def _get_remote_column(self, local_cls, local_column):
     try:
         return self._l_to_r[local_cls].get(local_column)
     except KeyError:
         map = {}
         for local_prop, _remote_column in iter_zip(self.local_key,
                                                    self.remote_key):
             map[local_prop.__get__(None, local_cls)] = _remote_column
         return self._l_to_r.setdefault(local_cls, map).get(local_column)
Exemple #6
0
 def get_insert_identity(self, primary_key, primary_variables):
     equals = []
     for column, variable in iter_zip(primary_key, primary_variables):
         if not variable.is_defined():
             # The Select here prevents PostgreSQL from going nuts and
             # performing a sequential scan when there *is* an index.
             # http://tinyurl.com/2n8mv3
             variable = Select(currval(column))
         equals.append(Eq(column, variable))
     return And(*equals)
Exemple #7
0
    def execute(self, statement, params=None, noresult=False):
        """Execute a statement with the given parameters.

        This extends the L{Connection.execute} method to add support
        for automatic retrieval of inserted primary keys to link
        in-memory objects with their specific rows.
        """
        if (isinstance(statement, Insert) and
            self._database._version >= 80200 and
            statement.primary_variables is not Undef and
            statement.primary_columns is not Undef):

            # Here we decorate the Insert statement with a Returning
            # expression, so that we get back in the result the values
            # for the primary key just inserted.  This prevents a round
            # trip to the database for obtaining these values.

            result = Connection.execute(self, Returning(statement), params)
            for variable, value in iter_zip(statement.primary_variables,
                                            result.get_one()):
                result.set_variable(variable, value)
            return result

        return Connection.execute(self, statement, params, noresult)
Exemple #8
0
def assert_variables_equal(checked, expected):
    assert len(checked) == len(expected)
    for check, expect in iter_zip(checked, expected):
        assert check.__class__ == expect.__class__
        assert check.get() == expect.get()
Exemple #9
0
    def link(self, local, remote, setting=False):
        """Link objects to represent their relation.

        @param local: Object representing the I{local} side of the reference.

        @param remote: Object representing the I{remote} side of the reference,
            or the actual value to be set as the local key.

        @param setting: Pass true when the relationship is being newly created.
        """
        local_info = get_obj_info(local)

        try:
            remote_info = get_obj_info(remote)
        except ClassInfoError:
            # Must be a plain key. Just set it.
            # XXX I guess this is broken if self.on_remote is True.
            local_variables = self.get_local_variables(local)
            if type(remote) is not tuple:
                remote = (remote,)
            assert len(remote) == len(local_variables)
            for variable, value in iter_zip(local_variables, remote):
                variable.set(value)
            return

        local_store = Store.of(local)
        remote_store = Store.of(remote)

        if setting:
            if local_store is None:
                if remote_store is None:
                    local_info.event.hook("added", self._add_all, local_info)
                    remote_info.event.hook("added", self._add_all, local_info)
                else:
                    remote_store.add(local)
                    local_store = remote_store
            elif remote_store is None:
                local_store.add(remote)
            elif local_store is not remote_store:
                raise WrongStoreError("%r and %r cannot be linked because they "
                                      "are in different stores." %
                                      (local, remote))

        # In cases below, we maintain a reference to the remote object
        # to make sure it won't get deallocated while the link is active.
        relation_data = local_info.get(self)
        if self.many:
            if relation_data is None:
                relation_data = local_info[self] = {"remote":
                                                    {remote_info: remote}}
            else:
                relation_data["remote"][remote_info] = remote
        else:
            if relation_data is None:
                relation_data = local_info[self] = {"remote": remote}
            else:
                old_remote = relation_data.get("remote")
                if old_remote is not None:
                    self.unlink(local_info, get_obj_info(old_remote))
                relation_data["remote"] = remote

        if setting:
            local_vars = local_info.variables
            remote_vars = remote_info.variables
            pairs = iter_zip(self._get_local_columns(local.__class__),
                             self.remote_key)
            if self.on_remote:
                local_has_changed = False
                for local_column, remote_column in pairs:
                    local_var = local_vars[local_column]
                    if not local_var.is_defined():
                        remote_vars[remote_column].set(PendingReferenceValue)
                    else:
                        remote_vars[remote_column].set(local_var.get())
                    if local_var.has_changed():
                        local_has_changed = True

                if local_has_changed:
                    self._add_flush_order(local_info, remote_info)

                local_info.event.hook("changed", self._track_local_changes,
                                      remote_info)
                local_info.event.hook("flushed", self._break_on_local_flushed,
                                      remote_info)
                #local_info.event.hook("removed", self._break_on_local_removed,
                #                      remote_info)
                remote_info.event.hook("removed", self._break_on_remote_removed,
                                       weakref.ref(local_info))
            else:
                remote_has_changed = False
                for local_column, remote_column in pairs:
                    remote_var = remote_vars[remote_column]
                    if not remote_var.is_defined():
                        local_vars[local_column].set(PendingReferenceValue)
                    else:
                        local_vars[local_column].set(remote_var.get())
                    if remote_var.has_changed():
                        remote_has_changed = True

                if remote_has_changed:
                    self._add_flush_order(local_info, remote_info,
                                          remote_first=True)

                remote_info.event.hook("changed", self._track_remote_changes,
                                       local_info)
                remote_info.event.hook("flushed", self._break_on_remote_flushed,
                                       local_info)
                #local_info.event.hook("removed", self._break_on_remote_removed,
                #                      local_info)

                local_info.event.hook("changed", self._break_on_local_diverged,
                                      remote_info)
        else:
            local_info.event.hook("changed", self._break_on_local_diverged,
                                  remote_info)
            remote_info.event.hook("changed", self._break_on_remote_diverged,
                                   weakref.ref(local_info))
            if self.on_remote:
                remote_info.event.hook("removed", self._break_on_remote_removed,
                                       weakref.ref(local_info))