@@ -46,17 +46,17 @@ def __init__(self,
4646 self ._organization_manager = organization_manager
4747 self ._cloud_projects = []
4848
49- def push_project (self , project : Path , encryption_action : Optional [ActionType ]= None , encryption_key : Optional [Path ]= None ) -> None :
49+ def push_project (self , project : Path , encryption_action : Optional [ActionType ]= None , encryption_key : Optional [Path ]= None , force : Optional [ bool ] = False ) -> None :
5050 """Pushes the given project from the local drive to the cloud.
5151
5252 It will also push every library referenced by the project and add or remove references.
5353
5454 :param project: path to the directory containing the local project that needs to be pushed
5555 """
5656 libraries = self ._project_manager .get_project_libraries (project )
57- self .push_projects ([project ], libraries , encryption_action , encryption_key )
57+ self .push_projects ([project ], libraries , encryption_action , encryption_key , force )
5858
59- def push_projects (self , projects_to_push : List [Path ], associated_libraries_to_push : Optional [List [Path ]]= [], encryption_action : Optional [ActionType ]= None , encryption_key : Optional [Path ]= None ) -> None :
59+ def push_projects (self , projects_to_push : List [Path ], associated_libraries_to_push : Optional [List [Path ]]= [], encryption_action : Optional [ActionType ]= None , encryption_key : Optional [Path ]= None , force : Optional [ bool ] = False ) -> None :
6060 """Pushes the given projects from the local drive to the cloud.
6161
6262 It will also push every library referenced by each project and add or remove references.
@@ -78,11 +78,13 @@ def push_projects(self, projects_to_push: List[Path], associated_libraries_to_pu
7878 relative_path = path .relative_to (Path .cwd ())
7979 try :
8080 self ._logger .info (f"[{ index } /{ len (all_projects_to_push )} ] Pushing '{ relative_path } '" )
81- self ._push_project (path , organization_id , encryption_action_value , encryption_key_value )
81+ self ._push_project (path , organization_id , encryption_action_value , encryption_key_value , force = force )
8282 except Exception as ex :
8383 from traceback import format_exc
8484 self ._logger .debug (format_exc ().strip ())
8585 self ._logger .warn (f"Cannot push '{ relative_path } ': { ex } " )
86+ if "write permission" in str (ex ).lower ():
87+ self ._logger .info ("Please pull any required changes and push with --force" )
8688
8789 def _get_local_libraries_cloud_ids (self , project_dir : Path ) -> List [int ]:
8890 project_config = self ._project_config_manager .get_project_config (project_dir )
@@ -95,7 +97,7 @@ def _get_local_libraries_cloud_ids(self, project_dir: Path) -> List[int]:
9597
9698 return local_libraries_cloud_ids
9799
98- def _push_project (self , project_path : Path , organization_id : str , encryption_action : Optional [ActionType ], encryption_key : Optional [Path ], suggested_rename_path : Path = None ) -> None :
100+ def _push_project (self , project_path : Path , organization_id : str , encryption_action : Optional [ActionType ], encryption_key : Optional [Path ], force : Optional [ bool ], suggested_rename_path : Path = None ) -> None :
99101 """Pushes a single local project to the cloud.
100102
101103 Raises an error with a descriptive message if the project cannot be pushed.
@@ -111,7 +113,6 @@ def _push_project(self, project_path: Path, organization_id: str, encryption_act
111113 if suggested_rename_path and suggested_rename_path != project_path :
112114 potential_new_name = suggested_rename_path .relative_to (Path .cwd ()).as_posix ()
113115
114-
115116 project_config = self ._project_config_manager .get_project_config (project_path )
116117 cloud_id = project_config .get ("cloud-id" )
117118 local_encryption_state = project_config .get ("encrypted" , False )
@@ -149,7 +150,7 @@ def _push_project(self, project_path: Path, organization_id: str, encryption_act
149150 if cloud_project .name != project_name :
150151 # cloud project name was changed. Repeat steps to validate the new name locally.
151152 self ._logger .info (f"Received new name '{ cloud_project .name } ' for project '{ project_name } ' from QuantConnect.com" )
152- self ._push_project (project_path , organization_id , encryption_action , encryption_key , Path .cwd () / cloud_project .name )
153+ self ._push_project (project_path , organization_id , encryption_action , encryption_key , force , Path .cwd () / cloud_project .name )
153154 return
154155
155156 self ._cloud_projects .append (cloud_project )
@@ -163,7 +164,7 @@ def _push_project(self, project_path: Path, organization_id: str, encryption_act
163164 encryption_key = local_encryption_key
164165 encryption_action = ActionType .ENCRYPT if local_encryption_state else ActionType .DECRYPT
165166 # Finalize pushing by updating locally modified metadata, files and libraries
166- self ._push_metadata (project_path , cloud_project , encryption_action , encryption_key )
167+ self ._push_metadata (project_path , cloud_project , encryption_action , encryption_key , force )
167168
168169 def _get_files (self , project : Path , encryption_action : Optional [ActionType ], encryption_key : Optional [Path ]) -> List [Dict [str , str ]]:
169170 """Pushes the files of a local project to the cloud.
@@ -193,7 +194,7 @@ def _get_files(self, project: Path, encryption_action: Optional[ActionType], enc
193194
194195 return files
195196
196- def _push_metadata (self , project : Path , cloud_project : QCProject , encryption_action : Optional [ActionType ], encryption_key : Optional [Path ]) -> None :
197+ def _push_metadata (self , project : Path , cloud_project : QCProject , encryption_action : Optional [ActionType ], encryption_key : Optional [Path ], force : Optional [ bool ] ) -> None :
197198 """Pushes local project description and parameters to the cloud.
198199
199200 Does nothing if the cloud is already up-to-date.
@@ -255,18 +256,22 @@ def _push_metadata(self, project: Path, cloud_project: QCProject, encryption_act
255256 encryption_key_id = get_project_key_hash (encryption_key )
256257 update_args ["encryption_key" ] = encryption_key_id
257258
259+ if not force :
260+ update_args ["code_source_id" ] = "cli"
258261 if update_args != {}:
259262 self ._api_client .projects .update (cloud_project .projectId , ** update_args )
260263
261264 if "encryption_key" in update_args :
262265 del update_args ["encryption_key" ]
266+
263267 updated_keys = list (update_args )
264268 if len (updated_keys ) == 1 :
265269 updated_keys_str = updated_keys [0 ]
266270 elif len (updated_keys ) == 2 :
267271 updated_keys_str = " and " .join (updated_keys )
268272 else :
269273 updated_keys_str = ", " .join (updated_keys [:- 1 ]) + f", and { updated_keys [- 1 ]} "
274+
270275 self ._logger .info (f"Successfully updated { updated_keys_str } for '{ cloud_project .name } '" )
271276
272277 def _get_cloud_project (self , project_id : int , organization_id : str ) -> QCProject :
0 commit comments