Merge lp:~mandel/desktopcouch/record_id into lp:desktopcouch

Proposed by Manuel de la Peña
Status: Merged
Approved by: Chad Miller
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~mandel/desktopcouch/record_id
Merge into: lp:desktopcouch
Diff against target: 44 lines (+15/-4)
2 files modified
desktopcouch/records/record.py (+3/-3)
desktopcouch/records/tests/test_record.py (+12/-1)
To merge this branch: bzr merge lp:~mandel/desktopcouch/record_id
Reviewer Review Type Date Requested Status
Chad Miller (community) Approve
Guillermo Gonzalez Pending
Review via email: mp+15753@code.launchpad.net

This proposal supersedes a proposal from 2009-11-30.

Commit message

Reverse broken logic in test of explicit record id sanity, and add a test that verifies it is correct.

To post a comment you must log in.
Revision history for this message
Manuel de la Peña (mandel) wrote : Posted in a previous version of this proposal

This branch ensures that the record_id is set whenever the +id key is present in the data pass to create a new record.

Revision history for this message
Manuel de la Peña (mandel) wrote : Posted in a previous version of this proposal

Ensured that when creating a record with a dict that contains the key '_id' its vlaue is used for record_id

Revision history for this message
Chad Miller (cmiller) wrote : Posted in a previous version of this proposal

Let's not use "warnings" module. Instead, import logging and "logging.warn(...)", usually.

In this case, though, I would be just as happy with an assertion.

if '_id' in data:
   if record_id is not None:
       assert data['_id'] == record_id
       ...

Maybe drop the "record_id" member and make record_id a @property like record_type. Maybe we can drop all "_id" from code except for the getter.

Do we need a setter property also?

review: Needs Fixing
Revision history for this message
Chad Miller (cmiller) wrote : Posted in a previous version of this proposal

> if '_id' in data:

if data.get('_id', None) is not None: # safer. Explicit {'_id':None} would fail.

Revision history for this message
Manuel de la Peña (mandel) wrote : Posted in a previous version of this proposal

> Let's not use "warnings" module. Instead, import logging and
> "logging.warn(...)", usually.
>
> In this case, though, I would be just as happy with an assertion.
>
> if '_id' in data:
> if record_id is not None:
> assert data['_id'] == record_id
> ...
>
> Maybe drop the "record_id" member and make record_id a @property like
> record_type. Maybe we can drop all "_id" from code except for the getter.
>
> Do we need a setter property also?

I agree with the comments, the new implementation uses an assertion. I have implemented the record_id as a property with a getter and a setter. The setter is needed to pass all current tests.

Revision history for this message
Chad Miller (cmiller) wrote : Posted in a previous version of this proposal

13 + if '_id' in data and record_id is not None:
14 + assert data['_id'] == record_id
15 + elif record_id is not None:
16 + self._data['_id'] = record_id

This would be cleaner with two levels of "if".

if record_id is not None: # Explicit setting, so try to use it.
   if data.get('_id', None) is not None: # Overwrite _id if it is None.
      self._data['_id'] = record_id
   else: # _id already set. Verify that the explicit value agrees.
      assert self._data['_id'] == record_id, "record_id doesn't match value in data."

Note the data.get() and the second parameter to assert .

Revision history for this message
Manuel de la Peña (mandel) wrote : Posted in a previous version of this proposal

> 13 + if '_id' in data and record_id is not None:
> 14 + assert data['_id'] == record_id
> 15 + elif record_id is not None:
> 16 + self._data['_id'] = record_id
>
>
> This would be cleaner with two levels of "if".
>
> if record_id is not None: # Explicit setting, so try to use it.
> if data.get('_id', None) is not None: # Overwrite _id if it is None.
> self._data['_id'] = record_id
> else: # _id already set. Verify that the explicit value agrees.
> assert self._data['_id'] == record_id, "record_id doesn't match value in
> data."
>
>
>
> Note the data.get() and the second parameter to assert .

What if the record_id is not None and the data['_id']is indeed None? we could try to put in the db {'_id':None} which will raise an exception.

We would have to do yet another check.

Revision history for this message
Manuel de la Peña (mandel) wrote : Posted in a previous version of this proposal

> > 13 + if '_id' in data and record_id is not None:
> > 14 + assert data['_id'] == record_id
> > 15 + elif record_id is not None:
> > 16 + self._data['_id'] = record_id
> >
> >
> > This would be cleaner with two levels of "if".
> >
> > if record_id is not None: # Explicit setting, so try to use it.
> > if data.get('_id', None) is not None: # Overwrite _id if it is None.
> > self._data['_id'] = record_id
> > else: # _id already set. Verify that the explicit value agrees.
> > assert self._data['_id'] == record_id, "record_id doesn't match value
> in
> > data."
> >
> >
> >
> > Note the data.get() and the second parameter to assert .
>
> What if the record_id is not None and the data['_id']is indeed None? we could
> try to put in the db {'_id':None} which will raise an exception.
>
> We would have to do yet another check.
Sorry, I just realized what you meant... override _id. Stupid Manuel!

Revision history for this message
Chad Miller (cmiller) wrote : Posted in a previous version of this proposal

Looks good to me. Thank you.

review: Approve
Revision history for this message
Guillermo Gonzalez (verterok) wrote : Posted in a previous version of this proposal

Looks good.

review: Approve
Revision history for this message
Manuel de la Peña (mandel) wrote : Posted in a previous version of this proposal

Major screw up from my part. The committed patch is wrong (major screw up from my port, I should have added a test!!!)

The if statement should be:

if record_id is not None: # Explicit setting, so try to use it.
   if data.get('_id', None) is None: # Overwrite _id if it is None.
      self._data['_id'] = record_id
   else: # _id already set. Verify that the explicit value agrees.
      assert self._data['_id'] == record_id, "record_id doesn't match value in data."

Otherwise we will always raise an exception since None is never equal to record_id!!

I have made the changes in the same lp branch with an addition of a test case to ensure that next time this does not happen.

Sorry for the screw up!

Manuel

Revision history for this message
Chad Miller (cmiller) wrote :

That's not the way assertFail works.

Record(data, record_id=record_id) raises an exception. The next line is skipped. The except-pass block should be an indicator that you're throwing away information.

Kill that entire try block.

You should assert that calling Record that way will raise an exception.

self.assertRaises(AssertionError, Record, data, record_id=record_id)
That calls Record (data...)
and makes the test fail if it does not raise an Assertion Error.

---

However, assert should be for impossible cases, not user errors. If a user can cause an error, we should raise a ValueError("record_id doesn't match value in data.") . I'm sorry if I gave you that code that way.

review: Needs Fixing
lp:~mandel/desktopcouch/record_id updated
114. By Manuel de la Peña

Removed the use of an assertion. Used assertRaise in test.

Revision history for this message
Manuel de la Peña (mandel) wrote :

> That's not the way assertFail works.
>
> Record(data, record_id=record_id) raises an exception. The next line is
> skipped. The except-pass block should be an indicator that you're throwing
> away information.
>
> Kill that entire try block.
>
> You should assert that calling Record that way will raise an exception.
>
> self.assertRaises(AssertionError, Record, data, record_id=record_id)
> That calls Record (data...)
> and makes the test fail if it does not raise an Assertion Error.

Done, seems logical
> ---
>
> However, assert should be for impossible cases, not user errors. If a user
> can cause an error, we should raise a ValueError("record_id doesn't match
> value in data.") . I'm sorry if I gave you that code that way.

Not a big deal, I should have argued about the use an exception better, surely my fault too.

Revision history for this message
Chad Miller (cmiller) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'desktopcouch/records/record.py'
2--- desktopcouch/records/record.py 2009-11-30 16:37:06 +0000
3+++ desktopcouch/records/record.py 2009-12-07 16:31:10 +0000
4@@ -253,13 +253,13 @@
5
6 if record_id is not None:
7 # Explicit setting, so try to use it.
8- if data.get('_id', None) is not None:
9+ if data.get('_id', None) is None:
10 # Overwrite _id if it is None.
11 self._data['_id'] = record_id
12 else:
13 # _id already set. Verify that the explicit value agrees.
14- assert self._data['_id'] == record_id, "record_id doesn't match value in data."
15-
16+ if self._data['_id'] != record_id:
17+ raise ValueError("record_id doesn't match value in data.")
18
19 def __getitem__(self, key):
20 if is_internal(key):
21
22=== modified file 'desktopcouch/records/tests/test_record.py'
23--- desktopcouch/records/tests/test_record.py 2009-11-18 18:43:41 +0000
24+++ desktopcouch/records/tests/test_record.py 2009-12-07 16:31:10 +0000
25@@ -208,7 +208,18 @@
26 globs = { "db": CouchDatabase('testing', create=True, ctx=ctx) }
27 results = doctest.testfile('../doc/records.txt', globs=globs)
28 self.assertEqual(0, results.failed)
29-
30+
31+ def test_record_id(self):
32+ data = {"_id":"recordid"}
33+ record = Record(data, record_type="url")
34+ self.assertEqual(data["_id"], record.record_id)
35+ data = {}
36+ record_id = "recordid"
37+ record = Record(data, record_type="url", record_id=record_id)
38+ self.assertEqual(record_id, record.record_id)
39+ data = {"_id":"differentid"}
40+ self.assertRaises(ValueError,
41+ Record, data, record_id=record_id, record_type="url")
42
43 class TestRecordFactory(TestCase):
44 """Test Record/Mergeable List factories."""

Subscribers

People subscribed via source and target branches