diff --git a/travo/gitlab.py b/travo/gitlab.py index 0cbc270c6d7b46aa66fe59a2589ff1ab6a5e665c..ada24b1f07a75517e6d1e6090a915a58ddff3dee 100644 --- a/travo/gitlab.py +++ b/travo/gitlab.py @@ -1,4 +1,5 @@ from dataclasses import dataclass, field, InitVar +import dataclasses import enum import getpass import io @@ -312,7 +313,7 @@ class GitLab: message = json.get("message", "") if message and message[0] != "2": raise ResourceNotFoundError(f"{cls.__name__} {path} not found: {json['message']}") - return cls(gitlab=self, **json) + return cls.from_json(gitlab=self, **json) def ensure_resource(self, cls: Type[R], @@ -362,7 +363,7 @@ class GitLab: if 'message' in json: raise RuntimeError(f"{resource_type} creation failed: {json['message']}") assert isinstance(json, dict) - return cls(gitlab=self, **json) + return cls.from_json(gitlab=self, **json) def confirm(self, message: str)-> bool: @@ -598,7 +599,7 @@ class GitLab: raise RuntimeError(f"User {username} not found") assert len(json) == 1 json = json[0] - return User(gitlab=self, **json) + return User.from_json(gitlab=self, **json) def git(self, args: Sequence[str], @@ -801,6 +802,20 @@ class Resource: gitlab: GitLab id: int + extra: dict + + @classmethod + def from_json(cls: Type[R], **json: Any) -> R: + """ + Instantiate the class but move unknown dafafields into the `extra` dict. + This makes it safer to initialize the resource with extra information from a future gitlab version. + """ + data = {} # the collected datafields from the class + for f in dataclasses.fields(cls): + if f.name in json: + data[f.name] = json.pop(f.name) + data["extra"] = json + return cls(**data) def get_api_url(self) -> str: return f"{self._resource_type_api_url}/{self.id}" @@ -860,10 +875,10 @@ class Resource: resource_type = typing_utils.get_args(type_hint)[0] if value is not None and not isinstance(value, resource_type): assert isinstance(value, dict) - value = resource_type(gitlab=self.gitlab, **value) + value = resource_type.from_json(gitlab=self.gitlab, **value) elif typing_utils.issubtype(type_hint, List[Resource]): resource_type = typing_utils.get_args(type_hint)[0] - value = [ resource_type(gitlab=self.gitlab, **v) for v in value ] + value = [ resource_type.from_json(gitlab=self.gitlab, **v) for v in value ] self.__dict__[key] = value def __setattr__(self, key: str, value: Any) -> None: @@ -992,11 +1007,6 @@ class Project(Resource): build_git_strategy: Optional[str ] = None restrict_user_defined_variables: Optional[bool] = None - container_registry_image_prefix: Optional[str ] = None - topics: Optional[list] = None - ci_job_token_scope_enabled: Optional[bool] = None - squash_option: Optional[str ] = None - keep_latest_artifact: Optional[bool] = None def ensure_fork(self, path: str, @@ -1081,7 +1091,7 @@ class Project(Resource): f"from {fork.path_with_namespace} " f"to {self.path_with_namespace} failed: " f"{json['message']}") - fork = Project(self.gitlab, **json) + fork = Project.from_json(gitlab=self.gitlab, **json) assert fork.forked_from_project is not None if not fork_of_fork: assert fork.forked_from_project.id == self.id @@ -1170,7 +1180,7 @@ class Project(Resource): )) if int(res.headers['X-Total']) > per_page: raise NotImplementedError("Too many forks; pagination not yet implemented") - forks = [Project(gitlab=self.gitlab, **fork) + forks = [Project.from_json(gitlab=self.gitlab, **fork) for fork in res.json()] if recursive: forks = forks + [subfork @@ -1236,7 +1246,7 @@ class Project(Resource): res = self.gitlab.get(f"/projects", data=q) page = res.headers.get("X-Next-Page") for json in res.json(): - other = Project(self.gitlab, **json) + other = Project.from_json(gitlab=self.gitlab, **json) if (other.forked_from_project is not None and (not deep or other.forked_from_project.id == self.id)): continue # Already registered as a fork of something @@ -1682,7 +1692,7 @@ class User(Resource): Note: this is paginated """ - return [Project(gitlab=self.gitlab, **project) + return [Project.from_json(gitlab=self.gitlab, **project) for project in self.gitlab.get(f"/users/{self.id}/projects").json()] class GitLabTest(GitLab):