def test_can_handle_json_error_from_delete(graph): node, = graph.create({}) non_existent_property = Resource(node.properties.resource.uri.string + "/foo") try: non_existent_property.delete() except GraphError: assert True else: assert False
def test_can_handle_other_error_from_delete(): with patch.object(_Resource, "delete") as mocked: mocked.side_effect = DodgyServerError resource = Resource("http://localhost:7474/db/data/node/spam") try: resource.delete() except GraphError as error: assert isinstance(error.__cause__, DodgyServerError) else: assert False
def test_can_handle_other_error_from_delete(): with patch.object(_Resource, "delete") as mocked: mocked.side_effect = DodgyServerError resource = Resource("http://localhost:7474/db/data/node/spam") try: resource.delete() except GraphError as error: assert isinstance(error.__cause__, DodgyServerError) else: assert False
def test_can_handle_409(graph): node_id = get_attached_node_id(graph) resource = Resource("http://localhost:7474/db/data/node/%s" % node_id) try: resource.delete() except GraphError as error: assert_error( error, (GraphError,), "org.neo4j.server.rest.web.OperationFailureException", (_ClientError, _Response), 409) else: assert False
def test_can_handle_json_error_from_delete(graph): node, = graph.create({}) non_existent_property = Resource(node.properties.resource.uri.string + "/foo") try: non_existent_property.delete() except GraphError as error: cause = error.__cause__ assert isinstance(cause, _ClientError) assert isinstance(cause, _Response) assert cause.status_code == 404 else: assert False
def test_can_handle_json_error_from_delete(graph): node, = graph.create({}) non_existent_property = Resource(node.properties.resource.uri.string + "/foo") try: non_existent_property.delete() except GraphError as error: cause = error.__cause__ assert isinstance(cause, _ClientError) assert isinstance(cause, _Response) assert cause.status_code == 404 else: assert False
class CypherTransaction(object): """ A transaction is a transient resource that allows multiple Cypher statements to be executed within a single server transaction. """ error_class = CypherTransactionError def __init__(self, uri): log.info("begin") self.statements = [] self.__begin = Resource(uri) self.__begin_commit = Resource(uri + "/commit") self.__execute = None self.__commit = None self.__finished = False self.graph = self.__begin.graph def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.commit() def __assert_unfinished(self): if self.__finished: raise Finished(self) @property def _id(self): """ The internal server ID of this transaction, if available. """ if self.__execute is None: return None else: return int(self.__execute.uri.path.segments[-1]) @property def finished(self): """ Indicates whether or not this transaction has been completed or is still open. :return: :py:const:`True` if this transaction has finished, :py:const:`False` otherwise """ return self.__finished def append(self, statement, parameters=None): """ Add a statement to the current queue of statements to be executed. :param statement: the statement to append :param parameters: a dictionary of execution parameters """ self.__assert_unfinished() p = {} if parameters: for key, value in parameters.items(): if isinstance(value, (Node, Rel, Relationship)): value = value._id p[key] = value # OrderedDict is used here to avoid statement/parameters ordering bug log.info("append %r %r", statement, p) self.statements.append(OrderedDict([ ("statement", statement), ("parameters", p), ("resultDataContents", ["REST"]), ])) def post(self, resource): self.__assert_unfinished() rs = resource.post({"statements": self.statements}) location = rs.location if location: self.__execute = Resource(location) j = rs.content rs.close() self.statements = [] if "commit" in j: self.__commit = Resource(j["commit"]) if "errors" in j: errors = j["errors"] if len(errors) >= 1: error = errors[0] raise self.error_class.hydrate(error) out = RecordListList() for result in j["results"]: columns = result["columns"] producer = RecordProducer(columns) out.append(RecordList(columns, [producer.produce(self.graph.hydrate(r["rest"])) for r in result["data"]])) return out def process(self): """ Send all pending statements to the server for execution, leaving the transaction open for further statements. Along with :meth:`append <.CypherTransaction.append>`, this method can be used to batch up a number of individual statements into a single HTTP request:: from py2neo import Graph graph = Graph() statement = "MERGE (n:Person {name:{N}}) RETURN n" tx = graph.cypher.begin() def add_names(*names): for name in names: tx.append(statement, {"N": name}) tx.process() add_names("Homer", "Marge", "Bart", "Lisa", "Maggie") add_names("Peter", "Lois", "Chris", "Meg", "Stewie") tx.commit() :return: list of results from pending statements """ log.info("process") return self.post(self.__execute or self.__begin) def commit(self): """ Send all pending statements to the server for execution and commit the transaction. :return: list of results from pending statements """ log.info("commit") try: return self.post(self.__commit or self.__begin_commit) finally: self.__finished = True def rollback(self): """ Rollback the current transaction. """ self.__assert_unfinished() log.info("rollback") try: if self.__execute: self.__execute.delete() finally: self.__finished = True
class CypherTransaction(object): """ A transaction is a transient resource that allows multiple Cypher statements to be executed within a single server transaction. """ error_class = CypherTransactionError def __init__(self, uri): log.info("begin") self.statements = [] self.__begin = Resource(uri) self.__begin_commit = Resource(uri + "/commit") self.__execute = None self.__commit = None self.__finished = False self.graph = self.__begin.graph def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.commit() def __assert_unfinished(self): if self.__finished: raise Finished(self) @property def _id(self): """ The internal server ID of this transaction, if available. """ if self.__execute is None: return None else: return int(self.__execute.uri.path.segments[-1]) @property def finished(self): """ Indicates whether or not this transaction has been completed or is still open. :return: :py:const:`True` if this transaction has finished, :py:const:`False` otherwise """ return self.__finished def append(self, statement, parameters=None, **kwparameters): """ Add a statement to the current queue of statements to be executed. :arg statement: the statement to append :arg parameters: a dictionary of execution parameters """ self.__assert_unfinished() s = ustr(statement) p = {} def add_parameters(params): if params: for k, v in dict(params).items(): if isinstance(v, (Node, Rel, Relationship)): v = v._id p[k] = v try: add_parameters(statement.parameters) except AttributeError: pass add_parameters(dict(parameters or {}, **kwparameters)) s, p = presubstitute(s, p) # OrderedDict is used here to avoid statement/parameters ordering bug log.info("append %r %r", s, p) self.statements.append( OrderedDict([ ("statement", s), ("parameters", p), ("resultDataContents", ["REST"]), ])) def post(self, resource): self.__assert_unfinished() rs = resource.post({"statements": self.statements}) location = rs.location if location: self.__execute = Resource(location) j = rs.content rs.close() self.statements = [] if "commit" in j: self.__commit = Resource(j["commit"]) if "errors" in j: errors = j["errors"] if len(errors) >= 1: error = errors[0] raise self.error_class.hydrate(error) out = RecordListList() for result in j["results"]: columns = result["columns"] producer = RecordProducer(columns) out.append( RecordList(columns, [ producer.produce(self.graph.hydrate(r["rest"])) for r in result["data"] ])) return out def process(self): """ Send all pending statements to the server for execution, leaving the transaction open for further statements. Along with :meth:`append <.CypherTransaction.append>`, this method can be used to batch up a number of individual statements into a single HTTP request:: from py2neo import Graph graph = Graph() statement = "MERGE (n:Person {name:{N}}) RETURN n" tx = graph.cypher.begin() def add_names(*names): for name in names: tx.append(statement, {"N": name}) tx.process() add_names("Homer", "Marge", "Bart", "Lisa", "Maggie") add_names("Peter", "Lois", "Chris", "Meg", "Stewie") tx.commit() :return: list of results from pending statements """ log.info("process") return self.post(self.__execute or self.__begin) def commit(self): """ Send all pending statements to the server for execution and commit the transaction. :return: list of results from pending statements """ log.info("commit") try: return self.post(self.__commit or self.__begin_commit) finally: self.__finished = True def rollback(self): """ Rollback the current transaction. """ self.__assert_unfinished() log.info("rollback") try: if self.__execute: self.__execute.delete() finally: self.__finished = True