)]}'
{"/COMMIT_MSG":[{"author":{"_account_id":15069,"name":"zhonghuali","email":"lizhonghua.li@huawei.com","username":"sloblee"},"change_message_id":"162a10b7b1adf322cfe2c295ae04c2a7f5de6854","unresolved":false,"context_lines":[{"line_number":17,"context_line":""},{"line_number":18,"context_line":"3. Add some other functions to it, such as transform it to dict."},{"line_number":19,"context_line":""},{"line_number":20,"context_line":"4. It is easy to chose members of it to be serialized by modifing"},{"line_number":21,"context_line":"the definition of property of \u0027Resource\u0027."},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"Change-Id: Ib5f3fea53bff9fe0a7c3b88339af26b70a6b7d3c"}],"source_content_type":"text/x-gerrit-commit-message","patch_set":2,"id":"bff0334d_7675c631","line":20,"range":{"start_line":20,"start_character":17,"end_line":20,"end_character":22},"updated":"2017-04-12 01:50:56.000000000","message":"choose?","commit_id":"4a47b5bf88f2562b9b3cc07df9646120ad1aa970"}],"karbor/resource.py":[{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"ffbd6d0db66e2b7fb2d824d885fab2ab5f774e50","unresolved":false,"context_lines":[{"line_number":13,"context_line":""},{"line_number":14,"context_line":"from collections import namedtuple"},{"line_number":15,"context_line":""},{"line_number":16,"context_line":"KEYS \u003d [\u0027type\u0027, \u0027id\u0027, \u0027name\u0027, \u0027extra_info\u0027]"},{"line_number":17,"context_line":"_Resource \u003d namedtuple(\"_Resource\", KEYS)"},{"line_number":18,"context_line":""},{"line_number":19,"context_line":""},{"line_number":20,"context_line":"class Resource(object):"},{"line_number":21,"context_line":"    def __init__(self, type, id, name, extra_info\u003dNone):"},{"line_number":22,"context_line":"        self._r \u003d _Resource(type, id, name, extra_info)"},{"line_number":23,"context_line":""},{"line_number":24,"context_line":"    def __setattr__(self, key, value):"},{"line_number":25,"context_line":"        if key !\u003d \u0027_r\u0027 or key in self.__dict__:"},{"line_number":26,"context_line":"            raise KeyError"},{"line_number":27,"context_line":"        return super(Resource, self).__setattr__(key, value)"},{"line_number":28,"context_line":""},{"line_number":29,"context_line":"    def __getattr__(self, item):"},{"line_number":30,"context_line":"        if item not in KEYS:"},{"line_number":31,"context_line":"            raise KeyError"},{"line_number":32,"context_line":"        return getattr(self._r, item)"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"    def __getitem__(self, item):"},{"line_number":35,"context_line":"        if not isinstance(item, int) or item \u003c 0 or item \u003e\u003d len(KEYS):"},{"line_number":36,"context_line":"            raise KeyError"},{"line_number":37,"context_line":"        return self._r[item]"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"    def __eq__(self, other):"},{"line_number":40,"context_line":"        return self._r \u003d\u003d other.resource"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":"    def to_dict(self):"},{"line_number":43,"context_line":"        return dict(zip(KEYS, self._r))"},{"line_number":44,"context_line":""},{"line_number":45,"context_line":"    @property"},{"line_number":46,"context_line":"    def resource(self):"},{"line_number":47,"context_line":"        return self._r"}],"source_content_type":"text/x-python","patch_set":1,"id":"bff0334d_a2e6cc43","line":47,"range":{"start_line":16,"start_character":0,"end_line":47,"end_character":22},"updated":"2017-04-06 13:12:49.000000000","message":"No need to use namedtuple here.\nUse just a class with __slots__","commit_id":"cd54664439fd6e9185cdb068f9a5895abad2a8d3"},{"author":{"_account_id":15069,"name":"zhonghuali","email":"lizhonghua.li@huawei.com","username":"sloblee"},"change_message_id":"162a10b7b1adf322cfe2c295ae04c2a7f5de6854","unresolved":false,"context_lines":[{"line_number":20,"context_line":"        self.id \u003d id"},{"line_number":21,"context_line":"        self.name \u003d name"},{"line_number":22,"context_line":"        self.extra_info \u003d extra_info"},{"line_number":23,"context_line":"        self._is_init \u003d 1"},{"line_number":24,"context_line":""},{"line_number":25,"context_line":"    def __setattr__(self, key, value):"},{"line_number":26,"context_line":"        if \u0027_is_init\u0027 in self.__dict__:"}],"source_content_type":"text/x-python","patch_set":2,"id":"bff0334d_34aa5618","line":23,"range":{"start_line":23,"start_character":8,"end_line":23,"end_character":25},"updated":"2017-04-12 01:50:56.000000000","message":"there are many ways can be followed to define the read-only attribution members clearly:\nhttp://stackoverflow.com/questions/3203286/how-to-create-a-read-only-class-property-in-python","commit_id":"4a47b5bf88f2562b9b3cc07df9646120ad1aa970"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"545dadd70e50b6ec5dc0068fb07bde2ed6b0c6bd","unresolved":false,"context_lines":[{"line_number":20,"context_line":"        self.id \u003d id"},{"line_number":21,"context_line":"        self.name \u003d name"},{"line_number":22,"context_line":"        self.extra_info \u003d extra_info"},{"line_number":23,"context_line":"        self._is_init \u003d 1"},{"line_number":24,"context_line":""},{"line_number":25,"context_line":"    def __setattr__(self, key, value):"},{"line_number":26,"context_line":"        if \u0027_is_init\u0027 in self.__dict__:"}],"source_content_type":"text/x-python","patch_set":2,"id":"9ff52f3f_7e7d28e6","line":23,"range":{"start_line":23,"start_character":8,"end_line":23,"end_character":25},"in_reply_to":"bff0334d_34aa5618","updated":"2017-04-13 07:53:52.000000000","message":"Done","commit_id":"4a47b5bf88f2562b9b3cc07df9646120ad1aa970"},{"author":{"_account_id":15069,"name":"zhonghuali","email":"lizhonghua.li@huawei.com","username":"sloblee"},"change_message_id":"162a10b7b1adf322cfe2c295ae04c2a7f5de6854","unresolved":false,"context_lines":[{"line_number":24,"context_line":""},{"line_number":25,"context_line":"    def __setattr__(self, key, value):"},{"line_number":26,"context_line":"        if \u0027_is_init\u0027 in self.__dict__:"},{"line_number":27,"context_line":"            raise KeyError"},{"line_number":28,"context_line":"        return super(Resource, self).__setattr__(key, value)"},{"line_number":29,"context_line":""},{"line_number":30,"context_line":"    def __getitem__(self, item):"}],"source_content_type":"text/x-python","patch_set":2,"id":"bff0334d_11bfa4b0","line":27,"range":{"start_line":27,"start_character":18,"end_line":27,"end_character":26},"updated":"2017-04-12 01:50:56.000000000","message":"suggest to raise AttributeError","commit_id":"4a47b5bf88f2562b9b3cc07df9646120ad1aa970"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"545dadd70e50b6ec5dc0068fb07bde2ed6b0c6bd","unresolved":false,"context_lines":[{"line_number":24,"context_line":""},{"line_number":25,"context_line":"    def __setattr__(self, key, value):"},{"line_number":26,"context_line":"        if \u0027_is_init\u0027 in self.__dict__:"},{"line_number":27,"context_line":"            raise KeyError"},{"line_number":28,"context_line":"        return super(Resource, self).__setattr__(key, value)"},{"line_number":29,"context_line":""},{"line_number":30,"context_line":"    def __getitem__(self, item):"}],"source_content_type":"text/x-python","patch_set":2,"id":"9ff52f3f_1e273c15","line":27,"range":{"start_line":27,"start_character":18,"end_line":27,"end_character":26},"in_reply_to":"bff0334d_11bfa4b0","updated":"2017-04-13 07:53:52.000000000","message":"Done","commit_id":"4a47b5bf88f2562b9b3cc07df9646120ad1aa970"},{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"40f6180a23fdd5cd933f62f388c0cc155c62dcba","unresolved":false,"context_lines":[{"line_number":20,"context_line":"        self.name \u003d name"},{"line_number":21,"context_line":"        self.extra_info \u003d extra_info"},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"    def __setattr__(self, key, value):"},{"line_number":24,"context_line":"        try:"},{"line_number":25,"context_line":"            getattr(self, key)"},{"line_number":26,"context_line":"        except AttributeError:"},{"line_number":27,"context_line":"            pass"},{"line_number":28,"context_line":"        else:"},{"line_number":29,"context_line":"            raise AttributeError()"},{"line_number":30,"context_line":""},{"line_number":31,"context_line":"        return super(Resource, self).__setattr__(key, value)"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":"    def __getitem__(self, item):"},{"line_number":34,"context_line":"        if not isinstance(item, int) or item \u003c 0 or ("}],"source_content_type":"text/x-python","patch_set":3,"id":"7ffa3b31_2d96c040","line":31,"range":{"start_line":23,"start_character":0,"end_line":31,"end_character":60},"updated":"2017-04-20 10:46:14.000000000","message":"What is that for?","commit_id":"b2c4b59c917444093011d0dedaa13ba0d9aa1411"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"e9c36d3e460a521d94116b5d0adaa0801c9e5edb","unresolved":false,"context_lines":[{"line_number":20,"context_line":"        self.name \u003d name"},{"line_number":21,"context_line":"        self.extra_info \u003d extra_info"},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"    def __setattr__(self, key, value):"},{"line_number":24,"context_line":"        try:"},{"line_number":25,"context_line":"            getattr(self, key)"},{"line_number":26,"context_line":"        except AttributeError:"},{"line_number":27,"context_line":"            pass"},{"line_number":28,"context_line":"        else:"},{"line_number":29,"context_line":"            raise AttributeError()"},{"line_number":30,"context_line":""},{"line_number":31,"context_line":"        return super(Resource, self).__setattr__(key, value)"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":"    def __getitem__(self, item):"},{"line_number":34,"context_line":"        if not isinstance(item, int) or item \u003c 0 or ("}],"source_content_type":"text/x-python","patch_set":3,"id":"7ffa3b31_f27434d1","line":31,"range":{"start_line":23,"start_character":0,"end_line":31,"end_character":60},"in_reply_to":"7ffa3b31_2d96c040","updated":"2017-04-20 11:36:04.000000000","message":"To avoid update the attributes of instance after it is created.","commit_id":"b2c4b59c917444093011d0dedaa13ba0d9aa1411"},{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"40f6180a23fdd5cd933f62f388c0cc155c62dcba","unresolved":false,"context_lines":[{"line_number":30,"context_line":""},{"line_number":31,"context_line":"        return super(Resource, self).__setattr__(key, value)"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":"    def __getitem__(self, item):"},{"line_number":34,"context_line":"        if not isinstance(item, int) or item \u003c 0 or ("},{"line_number":35,"context_line":"                item \u003e\u003d len(self.__slots__)):"},{"line_number":36,"context_line":"            raise KeyError"},{"line_number":37,"context_line":"        return getattr(self, self.__slots__[item])"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"    def __eq__(self, other):"},{"line_number":40,"context_line":"        return self.resource \u003d\u003d other.resource"}],"source_content_type":"text/x-python","patch_set":3,"id":"7ffa3b31_0db0a4dc","line":37,"range":{"start_line":33,"start_character":0,"end_line":37,"end_character":50},"updated":"2017-04-20 10:46:14.000000000","message":"Why do we need to get attributes by id?","commit_id":"b2c4b59c917444093011d0dedaa13ba0d9aa1411"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"e9c36d3e460a521d94116b5d0adaa0801c9e5edb","unresolved":false,"context_lines":[{"line_number":30,"context_line":""},{"line_number":31,"context_line":"        return super(Resource, self).__setattr__(key, value)"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":"    def __getitem__(self, item):"},{"line_number":34,"context_line":"        if not isinstance(item, int) or item \u003c 0 or ("},{"line_number":35,"context_line":"                item \u003e\u003d len(self.__slots__)):"},{"line_number":36,"context_line":"            raise KeyError"},{"line_number":37,"context_line":"        return getattr(self, self.__slots__[item])"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"    def __eq__(self, other):"},{"line_number":40,"context_line":"        return self.resource \u003d\u003d other.resource"}],"source_content_type":"text/x-python","patch_set":3,"id":"7ffa3b31_924df09b","line":37,"range":{"start_line":33,"start_character":0,"end_line":37,"end_character":50},"in_reply_to":"7ffa3b31_0db0a4dc","updated":"2017-04-20 11:36:04.000000000","message":"Because the original tuple can be accessed by index.","commit_id":"b2c4b59c917444093011d0dedaa13ba0d9aa1411"},{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"e21c5d246057e613436a348643ba1ecb3ddd9a7a","unresolved":false,"context_lines":[{"line_number":30,"context_line":""},{"line_number":31,"context_line":"        return super(Resource, self).__setattr__(key, value)"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":"    def __getitem__(self, item):"},{"line_number":34,"context_line":"        if not isinstance(item, int) or item \u003c 0 or ("},{"line_number":35,"context_line":"                item \u003e\u003d len(self.__slots__)):"},{"line_number":36,"context_line":"            raise KeyError"},{"line_number":37,"context_line":"        return getattr(self, self.__slots__[item])"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"    def __eq__(self, other):"},{"line_number":40,"context_line":"        return self.resource \u003d\u003d other.resource"}],"source_content_type":"text/x-python","patch_set":3,"id":"7ffa3b31_663e228c","line":37,"range":{"start_line":33,"start_character":0,"end_line":37,"end_character":50},"in_reply_to":"7ffa3b31_924df09b","updated":"2017-04-20 13:01:13.000000000","message":"Do anybody uses that?","commit_id":"b2c4b59c917444093011d0dedaa13ba0d9aa1411"},{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"40f6180a23fdd5cd933f62f388c0cc155c62dcba","unresolved":false,"context_lines":[{"line_number":39,"context_line":"    def __eq__(self, other):"},{"line_number":40,"context_line":"        return self.resource \u003d\u003d other.resource"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":"    def __hash__(self):"},{"line_number":43,"context_line":"        return hash(id(self))"},{"line_number":44,"context_line":""},{"line_number":45,"context_line":"    def to_dict(self):"},{"line_number":46,"context_line":"        return {item: getattr(self, item) for item in self.__slots__}"}],"source_content_type":"text/x-python","patch_set":3,"id":"7ffa3b31_adcf7054","line":43,"range":{"start_line":42,"start_character":0,"end_line":43,"end_character":29},"updated":"2017-04-20 10:46:14.000000000","message":"Why do we need to override __hash__?","commit_id":"b2c4b59c917444093011d0dedaa13ba0d9aa1411"},{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"e21c5d246057e613436a348643ba1ecb3ddd9a7a","unresolved":false,"context_lines":[{"line_number":39,"context_line":"    def __eq__(self, other):"},{"line_number":40,"context_line":"        return self.resource \u003d\u003d other.resource"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":"    def __hash__(self):"},{"line_number":43,"context_line":"        return hash(id(self))"},{"line_number":44,"context_line":""},{"line_number":45,"context_line":"    def to_dict(self):"},{"line_number":46,"context_line":"        return {item: getattr(self, item) for item in self.__slots__}"}],"source_content_type":"text/x-python","patch_set":3,"id":"7ffa3b31_e65112db","line":43,"range":{"start_line":42,"start_character":0,"end_line":43,"end_character":29},"in_reply_to":"7ffa3b31_954efa6c","updated":"2017-04-20 13:01:13.000000000","message":"https://docs.python.org/3.3/reference/datamodel.html#object.__hash__\n\nIf a class that overrides __eq__() needs to retain the implementation of __hash__() from a parent class, the interpreter must be told this explicitly by setting __hash__ \u003d \u003cParentClass\u003e.__hash__.","commit_id":"b2c4b59c917444093011d0dedaa13ba0d9aa1411"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"e9c36d3e460a521d94116b5d0adaa0801c9e5edb","unresolved":false,"context_lines":[{"line_number":39,"context_line":"    def __eq__(self, other):"},{"line_number":40,"context_line":"        return self.resource \u003d\u003d other.resource"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":"    def __hash__(self):"},{"line_number":43,"context_line":"        return hash(id(self))"},{"line_number":44,"context_line":""},{"line_number":45,"context_line":"    def to_dict(self):"},{"line_number":46,"context_line":"        return {item: getattr(self, item) for item in self.__slots__}"}],"source_content_type":"text/x-python","patch_set":3,"id":"7ffa3b31_954efa6c","line":43,"range":{"start_line":42,"start_character":0,"end_line":43,"end_character":29},"in_reply_to":"7ffa3b31_adcf7054","updated":"2017-04-20 11:36:04.000000000","message":"Because the unit tests can not pass when run in python 3. In python 3, the class should override __hash__ when the __eq__ is overrided.","commit_id":"b2c4b59c917444093011d0dedaa13ba0d9aa1411"},{"author":{"_account_id":17151,"name":"chenying","email":"chenyingko@gmail.com","username":"chenying"},"change_message_id":"6da33b31916ae70b36fa3360e0844d0ad96f9aca","unresolved":false,"context_lines":[{"line_number":22,"context_line":"        self.name \u003d name"},{"line_number":23,"context_line":"        self.extra_info \u003d extra_info"},{"line_number":24,"context_line":""},{"line_number":25,"context_line":"    def __setattr__(self, key, value):"},{"line_number":26,"context_line":"        try:"},{"line_number":27,"context_line":"            getattr(self, key)"},{"line_number":28,"context_line":"        except AttributeError:"}],"source_content_type":"text/x-python","patch_set":5,"id":"5ff73747_938e50ce","line":25,"range":{"start_line":25,"start_character":4,"end_line":25,"end_character":38},"updated":"2017-04-26 09:08:24.000000000","message":"It means that after the resource being initializing, the property of resource is read-only. It looks good.","commit_id":"a8c071ac9ed7510ca4aa8457d701f2f27dae1e04"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"c5ae9de799f927acf71b679f49995e46521cf600","unresolved":false,"context_lines":[{"line_number":22,"context_line":"        self.name \u003d name"},{"line_number":23,"context_line":"        self.extra_info \u003d extra_info"},{"line_number":24,"context_line":""},{"line_number":25,"context_line":"    def __setattr__(self, key, value):"},{"line_number":26,"context_line":"        try:"},{"line_number":27,"context_line":"            getattr(self, key)"},{"line_number":28,"context_line":"        except AttributeError:"}],"source_content_type":"text/x-python","patch_set":5,"id":"5ff73747_ce20abb3","line":25,"range":{"start_line":25,"start_character":4,"end_line":25,"end_character":38},"in_reply_to":"5ff73747_938e50ce","updated":"2017-04-26 09:44:01.000000000","message":"correct!","commit_id":"a8c071ac9ed7510ca4aa8457d701f2f27dae1e04"},{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"fe80116575a841ae4c3e6d9d2f8768fc047bbd2e","unresolved":false,"context_lines":[{"line_number":12,"context_line":""},{"line_number":13,"context_line":""},{"line_number":14,"context_line":"class Resource(object):"},{"line_number":15,"context_line":"    __slots__ \u003d (\u0027type\u0027, \u0027id\u0027, \u0027name\u0027, \u0027extra_info\u0027)"},{"line_number":16,"context_line":""},{"line_number":17,"context_line":"    def __init__(self, type, id, name, extra_info\u003dNone):"},{"line_number":18,"context_line":"        self.type \u003d type"}],"source_content_type":"text/x-python","patch_set":10,"id":"ff0f0b1f_b2c22933","line":15,"range":{"start_line":15,"start_character":0,"end_line":15,"end_character":52},"updated":"2017-05-24 09:27:45.000000000","message":"__slots__ needs to include methods as well (such as \u0027to_dict\u0027, \u0027key\u0027)","commit_id":"9d8b0f6c507e3809a154a03ea00d0fc0c22b6bd8"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"c2a032c09f4648e2a41f3700dab5b17aba8a3cd0","unresolved":false,"context_lines":[{"line_number":12,"context_line":""},{"line_number":13,"context_line":""},{"line_number":14,"context_line":"class Resource(object):"},{"line_number":15,"context_line":"    __slots__ \u003d (\u0027type\u0027, \u0027id\u0027, \u0027name\u0027, \u0027extra_info\u0027)"},{"line_number":16,"context_line":""},{"line_number":17,"context_line":"    def __init__(self, type, id, name, extra_info\u003dNone):"},{"line_number":18,"context_line":"        self.type \u003d type"}],"source_content_type":"text/x-python","patch_set":10,"id":"df140735_e7df2ed4","line":15,"range":{"start_line":15,"start_character":0,"end_line":15,"end_character":52},"in_reply_to":"ff0f0b1f_30b7eef7","updated":"2017-05-31 01:53:49.000000000","message":"It seams that it is fault to add \u0027to_dict\u0027 to __slots__ in python3. Because It will raise the exception like \u0027ValueError: \u0027to_dict\u0027 in __slots__ conflicts with class variable\u0027","commit_id":"9d8b0f6c507e3809a154a03ea00d0fc0c22b6bd8"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"1ac1571a7f9f269d7cfe39212b8628fd8f0c48e0","unresolved":false,"context_lines":[{"line_number":12,"context_line":""},{"line_number":13,"context_line":""},{"line_number":14,"context_line":"class Resource(object):"},{"line_number":15,"context_line":"    __slots__ \u003d (\u0027type\u0027, \u0027id\u0027, \u0027name\u0027, \u0027extra_info\u0027)"},{"line_number":16,"context_line":""},{"line_number":17,"context_line":"    def __init__(self, type, id, name, extra_info\u003dNone):"},{"line_number":18,"context_line":"        self.type \u003d type"}],"source_content_type":"text/x-python","patch_set":10,"id":"ff0f0b1f_30b7eef7","line":15,"range":{"start_line":15,"start_character":0,"end_line":15,"end_character":52},"in_reply_to":"ff0f0b1f_b2c22933","updated":"2017-05-25 08:53:58.000000000","message":"Done","commit_id":"9d8b0f6c507e3809a154a03ea00d0fc0c22b6bd8"},{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"fe80116575a841ae4c3e6d9d2f8768fc047bbd2e","unresolved":false,"context_lines":[{"line_number":21,"context_line":"        self.extra_info \u003d extra_info"},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"    def __setattr__(self, key, value):"},{"line_number":24,"context_line":"        try:"},{"line_number":25,"context_line":"            getattr(self, key)"},{"line_number":26,"context_line":"        except AttributeError:"},{"line_number":27,"context_line":"            pass"},{"line_number":28,"context_line":"        else:"},{"line_number":29,"context_line":"            raise AttributeError()"},{"line_number":30,"context_line":""},{"line_number":31,"context_line":"        return super(Resource, self).__setattr__(key, value)"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":"    def __hash__(self):"},{"line_number":34,"context_line":"        return hash(self.key)"}],"source_content_type":"text/x-python","patch_set":10,"id":"ff0f0b1f_52be2db4","line":31,"range":{"start_line":24,"start_character":0,"end_line":31,"end_character":60},"updated":"2017-05-24 09:27:45.000000000","message":"in the current implementation, trying to set extra_info will fail as well.\nwhy not just do:\n    if key not in (\u0027type\u0027, \u0027id\u0027, \u0027name\u0027, \u0027extra_info\u0027):\n        raise AttributeError()\n    else:\n        return return super(Resource, self).__setattr__(key, value)","commit_id":"9d8b0f6c507e3809a154a03ea00d0fc0c22b6bd8"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"1ac1571a7f9f269d7cfe39212b8628fd8f0c48e0","unresolved":false,"context_lines":[{"line_number":21,"context_line":"        self.extra_info \u003d extra_info"},{"line_number":22,"context_line":""},{"line_number":23,"context_line":"    def __setattr__(self, key, value):"},{"line_number":24,"context_line":"        try:"},{"line_number":25,"context_line":"            getattr(self, key)"},{"line_number":26,"context_line":"        except AttributeError:"},{"line_number":27,"context_line":"            pass"},{"line_number":28,"context_line":"        else:"},{"line_number":29,"context_line":"            raise AttributeError()"},{"line_number":30,"context_line":""},{"line_number":31,"context_line":"        return super(Resource, self).__setattr__(key, value)"},{"line_number":32,"context_line":""},{"line_number":33,"context_line":"    def __hash__(self):"},{"line_number":34,"context_line":"        return hash(self.key)"}],"source_content_type":"text/x-python","patch_set":10,"id":"ff0f0b1f_500dc2e5","line":31,"range":{"start_line":24,"start_character":0,"end_line":31,"end_character":60},"in_reply_to":"ff0f0b1f_52be2db4","updated":"2017-05-25 08:53:58.000000000","message":"1. \u0027trying to set extra_info will fail as well\u0027, sorry, not understand. if you create an instance, and will fail to update any attribute of that instance. It works as tuple.\n2. The instance can be modified if run with your codes. It does not match the design.","commit_id":"9d8b0f6c507e3809a154a03ea00d0fc0c22b6bd8"},{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"fe80116575a841ae4c3e6d9d2f8768fc047bbd2e","unresolved":false,"context_lines":[{"line_number":39,"context_line":"    def to_dict(self):"},{"line_number":40,"context_line":"        return {item: getattr(self, item) for item in self.__slots__}"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":"    def to_list(self):"},{"line_number":43,"context_line":"        return [getattr(self, item) for item in self.__slots__]"},{"line_number":44,"context_line":""},{"line_number":45,"context_line":"    @property"},{"line_number":46,"context_line":"    def key(self):"}],"source_content_type":"text/x-python","patch_set":10,"id":"ff0f0b1f_1ea42207","line":43,"range":{"start_line":42,"start_character":0,"end_line":43,"end_character":63},"updated":"2017-05-24 09:27:45.000000000","message":"We don\u0027t really need \u0027to_list()\u0027 to be part of the interface for Resource. Just get the attributes where you need them.","commit_id":"9d8b0f6c507e3809a154a03ea00d0fc0c22b6bd8"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"1ac1571a7f9f269d7cfe39212b8628fd8f0c48e0","unresolved":false,"context_lines":[{"line_number":39,"context_line":"    def to_dict(self):"},{"line_number":40,"context_line":"        return {item: getattr(self, item) for item in self.__slots__}"},{"line_number":41,"context_line":""},{"line_number":42,"context_line":"    def to_list(self):"},{"line_number":43,"context_line":"        return [getattr(self, item) for item in self.__slots__]"},{"line_number":44,"context_line":""},{"line_number":45,"context_line":"    @property"},{"line_number":46,"context_line":"    def key(self):"}],"source_content_type":"text/x-python","patch_set":10,"id":"ff0f0b1f_70c1e653","line":43,"range":{"start_line":42,"start_character":0,"end_line":43,"end_character":63},"in_reply_to":"ff0f0b1f_1ea42207","updated":"2017-05-25 08:53:58.000000000","message":"Done","commit_id":"9d8b0f6c507e3809a154a03ea00d0fc0c22b6bd8"}],"karbor/services/protection/graph.py":[{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"40f6180a23fdd5cd933f62f388c0cc155c62dcba","unresolved":false,"context_lines":[{"line_number":140,"context_line":"    Allocate a serialized id (sid) for every node and build an adjacency list,"},{"line_number":141,"context_line":"    suitable for graph unpacking."},{"line_number":142,"context_line":"    \"\"\""},{"line_number":143,"context_line":"    def __init__(self, adjacency_list, nodes_dict, get_node_value):"},{"line_number":144,"context_line":"        super(PackGraphWalker, self).__init__()"},{"line_number":145,"context_line":"        self._sid_counter \u003d 0"},{"line_number":146,"context_line":"        self._node_to_sid \u003d {}"},{"line_number":147,"context_line":"        self._adjacency_list \u003d adjacency_list"},{"line_number":148,"context_line":"        self._sid_to_node \u003d nodes_dict"},{"line_number":149,"context_line":"        self._get_node_value \u003d get_node_value"},{"line_number":150,"context_line":""},{"line_number":151,"context_line":"    def on_node_enter(self, node, already_visited):"},{"line_number":152,"context_line":"        pass"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":"    def on_node_exit(self, node):"},{"line_number":155,"context_line":"        def key_serialize(key):"},{"line_number":156,"context_line":"            return hex(key)"},{"line_number":157,"context_line":""},{"line_number":158,"context_line":"        def get_node_value(node):"},{"line_number":159,"context_line":"            return node.value if not self._get_node_value else ("},{"line_number":160,"context_line":"                self._get_node_value(node))"},{"line_number":161,"context_line":""},{"line_number":162,"context_line":"        if node not in self._node_to_sid:"},{"line_number":163,"context_line":"            node_sid \u003d self._sid_counter"},{"line_number":164,"context_line":"            self._sid_counter +\u003d 1"},{"line_number":165,"context_line":"            self._node_to_sid[node] \u003d node_sid"},{"line_number":166,"context_line":"            self._sid_to_node[key_serialize(node_sid)] \u003d ("},{"line_number":167,"context_line":"                get_node_value(node))"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":"            if len(node.child_nodes) \u003e 0:"},{"line_number":170,"context_line":"                children_sids \u003d map(lambda node:"},{"line_number":171,"context_line":"                                    key_serialize(self._node_to_sid[node]),"},{"line_number":172,"context_line":"                                    node.child_nodes)"},{"line_number":173,"context_line":"                self._adjacency_list.append("},{"line_number":174,"context_line":"                    (key_serialize(node_sid), tuple(children_sids))"},{"line_number":175,"context_line":"                )"},{"line_number":176,"context_line":""},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"def pack_graph(start_nodes, get_node_value\u003dNone):"},{"line_number":179,"context_line":"    \"\"\"Return a PackedGraph from a list of GraphNodes"},{"line_number":180,"context_line":""},{"line_number":181,"context_line":"    Packs a graph into a flat PackedGraph (nodes dictionary, adjacency list)."},{"line_number":182,"context_line":"    \"\"\""},{"line_number":183,"context_line":"    walker \u003d GraphWalker()"},{"line_number":184,"context_line":"    nodes_dict \u003d {}"},{"line_number":185,"context_line":"    adjacency_list \u003d []"},{"line_number":186,"context_line":"    packer \u003d PackGraphWalker(adjacency_list, nodes_dict, get_node_value)"},{"line_number":187,"context_line":"    walker.register_listener(packer)"},{"line_number":188,"context_line":"    walker.walk_graph(start_nodes)"},{"line_number":189,"context_line":"    return PackedGraph(nodes_dict, tuple(adjacency_list))"}],"source_content_type":"text/x-python","patch_set":3,"id":"7ffa3b31_48eb3a12","line":186,"range":{"start_line":143,"start_character":0,"end_line":186,"end_character":72},"updated":"2017-04-20 10:46:14.000000000","message":"How about, instead of using get_node_value, just store the actual class. Below, in json.dumps/json.loads - use \u0027default\u0027 parameters.","commit_id":"b2c4b59c917444093011d0dedaa13ba0d9aa1411"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"e9c36d3e460a521d94116b5d0adaa0801c9e5edb","unresolved":false,"context_lines":[{"line_number":140,"context_line":"    Allocate a serialized id (sid) for every node and build an adjacency list,"},{"line_number":141,"context_line":"    suitable for graph unpacking."},{"line_number":142,"context_line":"    \"\"\""},{"line_number":143,"context_line":"    def __init__(self, adjacency_list, nodes_dict, get_node_value):"},{"line_number":144,"context_line":"        super(PackGraphWalker, self).__init__()"},{"line_number":145,"context_line":"        self._sid_counter \u003d 0"},{"line_number":146,"context_line":"        self._node_to_sid \u003d {}"},{"line_number":147,"context_line":"        self._adjacency_list \u003d adjacency_list"},{"line_number":148,"context_line":"        self._sid_to_node \u003d nodes_dict"},{"line_number":149,"context_line":"        self._get_node_value \u003d get_node_value"},{"line_number":150,"context_line":""},{"line_number":151,"context_line":"    def on_node_enter(self, node, already_visited):"},{"line_number":152,"context_line":"        pass"},{"line_number":153,"context_line":""},{"line_number":154,"context_line":"    def on_node_exit(self, node):"},{"line_number":155,"context_line":"        def key_serialize(key):"},{"line_number":156,"context_line":"            return hex(key)"},{"line_number":157,"context_line":""},{"line_number":158,"context_line":"        def get_node_value(node):"},{"line_number":159,"context_line":"            return node.value if not self._get_node_value else ("},{"line_number":160,"context_line":"                self._get_node_value(node))"},{"line_number":161,"context_line":""},{"line_number":162,"context_line":"        if node not in self._node_to_sid:"},{"line_number":163,"context_line":"            node_sid \u003d self._sid_counter"},{"line_number":164,"context_line":"            self._sid_counter +\u003d 1"},{"line_number":165,"context_line":"            self._node_to_sid[node] \u003d node_sid"},{"line_number":166,"context_line":"            self._sid_to_node[key_serialize(node_sid)] \u003d ("},{"line_number":167,"context_line":"                get_node_value(node))"},{"line_number":168,"context_line":""},{"line_number":169,"context_line":"            if len(node.child_nodes) \u003e 0:"},{"line_number":170,"context_line":"                children_sids \u003d map(lambda node:"},{"line_number":171,"context_line":"                                    key_serialize(self._node_to_sid[node]),"},{"line_number":172,"context_line":"                                    node.child_nodes)"},{"line_number":173,"context_line":"                self._adjacency_list.append("},{"line_number":174,"context_line":"                    (key_serialize(node_sid), tuple(children_sids))"},{"line_number":175,"context_line":"                )"},{"line_number":176,"context_line":""},{"line_number":177,"context_line":""},{"line_number":178,"context_line":"def pack_graph(start_nodes, get_node_value\u003dNone):"},{"line_number":179,"context_line":"    \"\"\"Return a PackedGraph from a list of GraphNodes"},{"line_number":180,"context_line":""},{"line_number":181,"context_line":"    Packs a graph into a flat PackedGraph (nodes dictionary, adjacency list)."},{"line_number":182,"context_line":"    \"\"\""},{"line_number":183,"context_line":"    walker \u003d GraphWalker()"},{"line_number":184,"context_line":"    nodes_dict \u003d {}"},{"line_number":185,"context_line":"    adjacency_list \u003d []"},{"line_number":186,"context_line":"    packer \u003d PackGraphWalker(adjacency_list, nodes_dict, get_node_value)"},{"line_number":187,"context_line":"    walker.register_listener(packer)"},{"line_number":188,"context_line":"    walker.walk_graph(start_nodes)"},{"line_number":189,"context_line":"    return PackedGraph(nodes_dict, tuple(adjacency_list))"}],"source_content_type":"text/x-python","patch_set":3,"id":"7ffa3b31_75900e4b","line":186,"range":{"start_line":143,"start_character":0,"end_line":186,"end_character":72},"in_reply_to":"7ffa3b31_48eb3a12","updated":"2017-04-20 11:36:04.000000000","message":"I will try. It is a good suggestion. Thanks.","commit_id":"b2c4b59c917444093011d0dedaa13ba0d9aa1411"},{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"40f6180a23fdd5cd933f62f388c0cc155c62dcba","unresolved":false,"context_lines":[{"line_number":223,"context_line":""},{"line_number":224,"context_line":""},{"line_number":225,"context_line":"def serialize_resource_graph(resource_graph):"},{"line_number":226,"context_line":"    packed_resource_graph \u003d pack_graph("},{"line_number":227,"context_line":"        resource_graph, lambda node: node.value.resource)"},{"line_number":228,"context_line":"    return jsonutils.dumps(packed_resource_graph)"},{"line_number":229,"context_line":""},{"line_number":230,"context_line":""},{"line_number":231,"context_line":"def deserialize_resource_graph(serialized_resource_graph):"}],"source_content_type":"text/x-python","patch_set":3,"id":"7ffa3b31_08f1b201","line":228,"range":{"start_line":226,"start_character":0,"end_line":228,"end_character":49},"updated":"2017-04-20 10:46:14.000000000","message":"see above","commit_id":"b2c4b59c917444093011d0dedaa13ba0d9aa1411"},{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"5743273a2aef140f748a16ea82f731883b4759b2","unresolved":false,"context_lines":[{"line_number":45,"context_line":"            _(\"A loop was found in the graph\"))"},{"line_number":46,"context_line":""},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"class NodeSet(object):"},{"line_number":49,"context_line":"    def __init__(self, nodes):"},{"line_number":50,"context_line":"        self._nodes \u003d {self._node_key(n): n for n in nodes}"},{"line_number":51,"context_line":""},{"line_number":52,"context_line":"    def difference_update(self, nodes):"},{"line_number":53,"context_line":"        for n in nodes:"},{"line_number":54,"context_line":"            self._nodes.pop(self._node_key(n), None)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":"    def _node_key(self, node):"},{"line_number":57,"context_line":"        return (node.type, node.id, node.name)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":"    def __contains__(self, item):"},{"line_number":60,"context_line":"        return self._node_key(item) in self._nodes"},{"line_number":61,"context_line":""},{"line_number":62,"context_line":""},{"line_number":63,"context_line":"def _build_graph_rec(context, node):"}],"source_content_type":"text/x-python","patch_set":9,"id":"3f044301_339bbe66","line":60,"range":{"start_line":48,"start_character":0,"end_line":60,"end_character":50},"updated":"2017-05-11 11:46:15.000000000","message":"Why does python\u0027s set won\u0027t fit?","commit_id":"1ad9fe02fc9a564c879b7af5921829c4ae5b1b08"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"567ce5fbe5915b2c7d426e5ec93b77c4fe070416","unresolved":false,"context_lines":[{"line_number":45,"context_line":"            _(\"A loop was found in the graph\"))"},{"line_number":46,"context_line":""},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"class NodeSet(object):"},{"line_number":49,"context_line":"    def __init__(self, nodes):"},{"line_number":50,"context_line":"        self._nodes \u003d {self._node_key(n): n for n in nodes}"},{"line_number":51,"context_line":""},{"line_number":52,"context_line":"    def difference_update(self, nodes):"},{"line_number":53,"context_line":"        for n in nodes:"},{"line_number":54,"context_line":"            self._nodes.pop(self._node_key(n), None)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":"    def _node_key(self, node):"},{"line_number":57,"context_line":"        return (node.type, node.id, node.name)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":"    def __contains__(self, item):"},{"line_number":60,"context_line":"        return self._node_key(item) in self._nodes"},{"line_number":61,"context_line":""},{"line_number":62,"context_line":""},{"line_number":63,"context_line":"def _build_graph_rec(context, node):"}],"source_content_type":"text/x-python","patch_set":9,"id":"1f013ff3_e9138e0c","line":60,"range":{"start_line":48,"start_character":0,"end_line":60,"end_character":50},"in_reply_to":"1f013ff3_0eab6089","updated":"2017-05-16 11:08:41.000000000","message":"According to the comments you said on patch set 3(see the following), it is not a good choice.\nhttps://docs.python.org/3.3/reference/datamodel.html#object.__hash__\nIf a class that overrides __eq__() needs to retain the implementation of __hash__() from a parent class, the interpreter must be told this explicitly by setting __hash__ \u003d \u003cParentClass\u003e.__hash__.","commit_id":"1ad9fe02fc9a564c879b7af5921829c4ae5b1b08"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"0fc289d0f99cf7ccc216fdb93a84dd682ba45e4b","unresolved":false,"context_lines":[{"line_number":45,"context_line":"            _(\"A loop was found in the graph\"))"},{"line_number":46,"context_line":""},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"class NodeSet(object):"},{"line_number":49,"context_line":"    def __init__(self, nodes):"},{"line_number":50,"context_line":"        self._nodes \u003d {self._node_key(n): n for n in nodes}"},{"line_number":51,"context_line":""},{"line_number":52,"context_line":"    def difference_update(self, nodes):"},{"line_number":53,"context_line":"        for n in nodes:"},{"line_number":54,"context_line":"            self._nodes.pop(self._node_key(n), None)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":"    def _node_key(self, node):"},{"line_number":57,"context_line":"        return (node.type, node.id, node.name)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":"    def __contains__(self, item):"},{"line_number":60,"context_line":"        return self._node_key(item) in self._nodes"},{"line_number":61,"context_line":""},{"line_number":62,"context_line":""},{"line_number":63,"context_line":"def _build_graph_rec(context, node):"}],"source_content_type":"text/x-python","patch_set":9,"id":"3f044301_9c44d648","line":60,"range":{"start_line":48,"start_character":0,"end_line":60,"end_character":50},"in_reply_to":"3f044301_339bbe66","updated":"2017-05-12 02:11:39.000000000","message":"Python\u0027s set uses \u0027hash(item)\u0027 as the index of its item. Each instance of Class have different hash value even if they have same attributes except that the Class overrides __hash__. Regarding to \u0027Resource\u0027. If use want to protect a server and volume which is attached to the server, then the workflow will fail if not use \u0027NodeSet\u0027. Because \u0027source_set.difference_update(child_nodes)\u0027 can not remove the volume when run _build_graph_rec with parameter of \u0027node\u003dserver\u0027. See the test case at tests/fullstack/test_checkpoints.test_server_attached_volume_protect_both","commit_id":"1ad9fe02fc9a564c879b7af5921829c4ae5b1b08"},{"author":{"_account_id":20883,"name":"Yuval Brik","email":"yuval@brik.org.il","username":"yuvalbr"},"change_message_id":"e4b27ae6eb7955b09a4963539888adc79edc1570","unresolved":false,"context_lines":[{"line_number":45,"context_line":"            _(\"A loop was found in the graph\"))"},{"line_number":46,"context_line":""},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"class NodeSet(object):"},{"line_number":49,"context_line":"    def __init__(self, nodes):"},{"line_number":50,"context_line":"        self._nodes \u003d {self._node_key(n): n for n in nodes}"},{"line_number":51,"context_line":""},{"line_number":52,"context_line":"    def difference_update(self, nodes):"},{"line_number":53,"context_line":"        for n in nodes:"},{"line_number":54,"context_line":"            self._nodes.pop(self._node_key(n), None)"},{"line_number":55,"context_line":""},{"line_number":56,"context_line":"    def _node_key(self, node):"},{"line_number":57,"context_line":"        return (node.type, node.id, node.name)"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":"    def __contains__(self, item):"},{"line_number":60,"context_line":"        return self._node_key(item) in self._nodes"},{"line_number":61,"context_line":""},{"line_number":62,"context_line":""},{"line_number":63,"context_line":"def _build_graph_rec(context, node):"}],"source_content_type":"text/x-python","patch_set":9,"id":"1f013ff3_0eab6089","line":60,"range":{"start_line":48,"start_character":0,"end_line":60,"end_character":50},"in_reply_to":"3f044301_9c44d648","updated":"2017-05-16 10:33:06.000000000","message":"Would overriding __hash__ to return the hash of the type, id, name in Resource fix that?","commit_id":"1ad9fe02fc9a564c879b7af5921829c4ae5b1b08"},{"author":{"_account_id":17151,"name":"chenying","email":"chenyingko@gmail.com","username":"chenying"},"change_message_id":"b6db4c8e3d111477ef55a2a7eca3b5607ca2d970","unresolved":false,"context_lines":[{"line_number":93,"context_line":""},{"line_number":94,"context_line":"def build_graph(start_nodes, get_child_nodes_func):"},{"line_number":95,"context_line":"    source_set \u003d None"},{"line_number":96,"context_line":"    for n in start_nodes:"},{"line_number":97,"context_line":"        source_set \u003d NodeSet(start_nodes) if isinstance(n, Resource) else ("},{"line_number":98,"context_line":"            set(start_nodes))"},{"line_number":99,"context_line":"        break"},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    context \u003d _GraphBuilderContext("},{"line_number":102,"context_line":"        source_set\u003dsource_set,"}],"source_content_type":"text/x-python","patch_set":9,"id":"3f044301_ace793ac","line":99,"range":{"start_line":96,"start_character":0,"end_line":99,"end_character":13},"updated":"2017-05-10 01:46:41.000000000","message":"Why not init a NodeSet instance directly? source_set \u003d NodeSet(start_nodes)\nIs there a situation that isinstance(n, Resource) is false?","commit_id":"1ad9fe02fc9a564c879b7af5921829c4ae5b1b08"},{"author":{"_account_id":18266,"name":"zengchen","email":"chenzeng2@huawei.com","username":"zengchen"},"change_message_id":"7bab1d4dca4db5780c547d7eaf3ba1e47f29940c","unresolved":false,"context_lines":[{"line_number":93,"context_line":""},{"line_number":94,"context_line":"def build_graph(start_nodes, get_child_nodes_func):"},{"line_number":95,"context_line":"    source_set \u003d None"},{"line_number":96,"context_line":"    for n in start_nodes:"},{"line_number":97,"context_line":"        source_set \u003d NodeSet(start_nodes) if isinstance(n, Resource) else ("},{"line_number":98,"context_line":"            set(start_nodes))"},{"line_number":99,"context_line":"        break"},{"line_number":100,"context_line":""},{"line_number":101,"context_line":"    context \u003d _GraphBuilderContext("},{"line_number":102,"context_line":"        source_set\u003dsource_set,"}],"source_content_type":"text/x-python","patch_set":9,"id":"3f044301_cf69b1f9","line":99,"range":{"start_line":96,"start_character":0,"end_line":99,"end_character":13},"in_reply_to":"3f044301_ace793ac","updated":"2017-05-10 03:47:12.000000000","message":"yes, there are many in unit tests, see test_graph.py","commit_id":"1ad9fe02fc9a564c879b7af5921829c4ae5b1b08"}]}
