From f1ccc8f10aec6bf5f34af460fcbb2e57eeca431c Mon Sep 17 00:00:00 2001 From: Tim Bauman Date: Fri, 5 Sep 2025 16:41:43 -0700 Subject: [PATCH 1/6] Use cls for update --- surge/projects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/surge/projects.py b/surge/projects.py index f215201..12bd27c 100644 --- a/surge/projects.py +++ b/surge/projects.py @@ -397,7 +397,7 @@ def update( endpoint = f"{PROJECTS_ENDPOINT}/{self.id}" response_json = self.put(endpoint, params, api_key=api_key) - return Project(**response_json) + return cls(**response_json) def workable_by_surger(self, surger_id, api_key: str = None): """ From cce030015e158ae55cfcd4059a2390ac72b8a124 Mon Sep 17 00:00:00 2001 From: Tim Bauman Date: Fri, 5 Sep 2025 16:50:21 -0700 Subject: [PATCH 2/6] update --- surge/projects.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/surge/projects.py b/surge/projects.py index 12bd27c..fbc4141 100644 --- a/surge/projects.py +++ b/surge/projects.py @@ -23,7 +23,6 @@ class Project(APIResource): def __init__(self, **kwargs): super().__init__() - self.__dict__.update(kwargs) if self.id is None: raise SurgeMissingIDError @@ -35,10 +34,16 @@ def __init__(self, **kwargs): # Convert timestamp str into datetime self.created_at = dateutil.parser.parse(self.created_at) + self._update(**kwargs) + + def _update(self, **kwargs): + self.__dict__.update(kwargs) + # If the Project has Questions, convert each into a Question object if hasattr(self, "questions"): self.questions = self._convert_questions_to_objects(self.questions) + def __str__(self): return f'' @@ -397,7 +402,7 @@ def update( endpoint = f"{PROJECTS_ENDPOINT}/{self.id}" response_json = self.put(endpoint, params, api_key=api_key) - return cls(**response_json) + return self._update(**response_json) def workable_by_surger(self, surger_id, api_key: str = None): """ From 4f81405774468f78fa62b8fadd2796b232b7a514 Mon Sep 17 00:00:00 2001 From: Tim Bauman Date: Mon, 8 Sep 2025 21:39:21 -0700 Subject: [PATCH 3/6] fix --- surge/projects.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/surge/projects.py b/surge/projects.py index fbc4141..61a085f 100644 --- a/surge/projects.py +++ b/surge/projects.py @@ -23,6 +23,7 @@ class Project(APIResource): def __init__(self, **kwargs): super().__init__() + self._update(**kwargs) if self.id is None: raise SurgeMissingIDError @@ -34,7 +35,6 @@ def __init__(self, **kwargs): # Convert timestamp str into datetime self.created_at = dateutil.parser.parse(self.created_at) - self._update(**kwargs) def _update(self, **kwargs): self.__dict__.update(kwargs) From ac84dfe51bed3762f4a8e5a74218188d2074d39a Mon Sep 17 00:00:00 2001 From: Tim Bauman Date: Mon, 8 Sep 2025 22:11:51 -0700 Subject: [PATCH 4/6] move --- surge/projects.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/surge/projects.py b/surge/projects.py index 61a085f..f536a6f 100644 --- a/surge/projects.py +++ b/surge/projects.py @@ -31,14 +31,14 @@ def __init__(self, **kwargs): if not (hasattr(self, "name") and self.name): raise SurgeMissingAttributeError - if hasattr(self, "created_at") and self.created_at: - # Convert timestamp str into datetime - self.created_at = dateutil.parser.parse(self.created_at) - def _update(self, **kwargs): self.__dict__.update(kwargs) + if hasattr(self, "created_at") and self.created_at: + # Convert timestamp str into datetime + self.created_at = dateutil.parser.parse(self.created_at) + # If the Project has Questions, convert each into a Question object if hasattr(self, "questions"): self.questions = self._convert_questions_to_objects(self.questions) From a2bd92a83fa887c632bd1940a07a38888617d414 Mon Sep 17 00:00:00 2001 From: Tim Bauman Date: Tue, 9 Sep 2025 09:45:18 -0700 Subject: [PATCH 5/6] tests --- surge/projects.py | 5 ++-- tests/test_projects.py | 54 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/surge/projects.py b/surge/projects.py index f536a6f..f5df596 100644 --- a/surge/projects.py +++ b/surge/projects.py @@ -365,7 +365,7 @@ def update( description: str = None, params: dict = {}, api_key: str = None, - ): + ) -> 'Project': """ Update an existing project @@ -402,7 +402,8 @@ def update( endpoint = f"{PROJECTS_ENDPOINT}/{self.id}" response_json = self.put(endpoint, params, api_key=api_key) - return self._update(**response_json) + self._update(**response_json) + return self def workable_by_surger(self, surger_id, api_key: str = None): """ diff --git a/tests/test_projects.py b/tests/test_projects.py index 6919256..c07231e 100644 --- a/tests/test_projects.py +++ b/tests/test_projects.py @@ -459,3 +459,57 @@ def test_update_with_fields_template(): {"fields_text": "ABC"}, api_key=None, ) + + +def test_update_modifies_project_in_place(): + """Test that update() modifies the project object in-place based on PUT response.""" + project = Project( + id="UPDATE_IN_PLACE", + name="Original Name", + status="draft", + payment_per_response=0.10 + ) + + # Store original object id to verify same instance is returned + original_object_id = id(project) + + # Mock the PUT response with updated attributes + put_response = { + "id": "UPDATE_IN_PLACE", + "name": "Updated Name", + "status": "launched", + "payment_per_response": 0.25, + "description": "New description added", + "instructions": "Updated instructions" + } + + with patch.object(Project, "put") as mock_put: + mock_put.return_value = put_response + + # Verify initial state + assert project.name == "Original Name" + assert project.status == "draft" + assert project.payment_per_response == 0.10 + assert not hasattr(project, "description") + assert not hasattr(project, "instructions") + + # Call update + updated_project = project.update(name="Updated Name") + + # Verify the same object instance is returned + assert updated_project is project + assert id(updated_project) == original_object_id + + # Verify project attributes were updated in-place from PUT response + assert project.name == "Updated Name" + assert project.status == "launched" + assert project.payment_per_response == 0.25 + assert project.description == "New description added" + assert project.instructions == "Updated instructions" + + # Verify PUT was called correctly + mock_put.assert_called_once_with( + f"{PROJECTS_ENDPOINT}/{project.id}", + {"name": "Updated Name"}, + api_key=None, + ) From f4ff1c7005f393793472de28b1336cdbb4a5fa42 Mon Sep 17 00:00:00 2001 From: Tim Bauman Date: Tue, 9 Sep 2025 09:47:40 -0700 Subject: [PATCH 6/6] docs --- surge/projects.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/surge/projects.py b/surge/projects.py index f5df596..dffda26 100644 --- a/surge/projects.py +++ b/surge/projects.py @@ -367,7 +367,7 @@ def update( api_key: str = None, ) -> 'Project': """ - Update an existing project + Update an existing project, and updates the project object in-place Arguments: name (str): Name of the project. @@ -380,7 +380,7 @@ def update( num_workers_per_task (int, optional): How many workers work on each task (i.e., how many responses per task). Returns: - project: new Project object + project: updated Project object """ params = {**params}