| | """ |
| | Data structures to interact with Discussions and Pull Requests on the Hub. |
| | |
| | See [the Discussions and Pull Requests guide](https://huggingface.co/docs/hub/repositories-pull-requests-discussions) |
| | for more information on Pull Requests, Discussions, and the community tab. |
| | """ |
| |
|
| | from dataclasses import dataclass |
| | from datetime import datetime |
| | from typing import List, Literal, Optional, Union |
| |
|
| | from . import constants |
| | from .utils import parse_datetime |
| |
|
| |
|
| | DiscussionStatus = Literal["open", "closed", "merged", "draft"] |
| |
|
| |
|
| | @dataclass |
| | class Discussion: |
| | """ |
| | A Discussion or Pull Request on the Hub. |
| | |
| | This dataclass is not intended to be instantiated directly. |
| | |
| | Attributes: |
| | title (`str`): |
| | The title of the Discussion / Pull Request |
| | status (`str`): |
| | The status of the Discussion / Pull Request. |
| | It must be one of: |
| | * `"open"` |
| | * `"closed"` |
| | * `"merged"` (only for Pull Requests ) |
| | * `"draft"` (only for Pull Requests ) |
| | num (`int`): |
| | The number of the Discussion / Pull Request. |
| | repo_id (`str`): |
| | The id (`"{namespace}/{repo_name}"`) of the repo on which |
| | the Discussion / Pull Request was open. |
| | repo_type (`str`): |
| | The type of the repo on which the Discussion / Pull Request was open. |
| | Possible values are: `"model"`, `"dataset"`, `"space"`. |
| | author (`str`): |
| | The username of the Discussion / Pull Request author. |
| | Can be `"deleted"` if the user has been deleted since. |
| | is_pull_request (`bool`): |
| | Whether or not this is a Pull Request. |
| | created_at (`datetime`): |
| | The `datetime` of creation of the Discussion / Pull Request. |
| | endpoint (`str`): |
| | Endpoint of the Hub. Default is https://huggingface.co. |
| | git_reference (`str`, *optional*): |
| | (property) Git reference to which changes can be pushed if this is a Pull Request, `None` otherwise. |
| | url (`str`): |
| | (property) URL of the discussion on the Hub. |
| | """ |
| |
|
| | title: str |
| | status: DiscussionStatus |
| | num: int |
| | repo_id: str |
| | repo_type: str |
| | author: str |
| | is_pull_request: bool |
| | created_at: datetime |
| | endpoint: str |
| |
|
| | @property |
| | def git_reference(self) -> Optional[str]: |
| | """ |
| | If this is a Pull Request , returns the git reference to which changes can be pushed. |
| | Returns `None` otherwise. |
| | """ |
| | if self.is_pull_request: |
| | return f"refs/pr/{self.num}" |
| | return None |
| |
|
| | @property |
| | def url(self) -> str: |
| | """Returns the URL of the discussion on the Hub.""" |
| | if self.repo_type is None or self.repo_type == constants.REPO_TYPE_MODEL: |
| | return f"{self.endpoint}/{self.repo_id}/discussions/{self.num}" |
| | return f"{self.endpoint}/{self.repo_type}s/{self.repo_id}/discussions/{self.num}" |
| |
|
| |
|
| | @dataclass |
| | class DiscussionWithDetails(Discussion): |
| | """ |
| | Subclass of [`Discussion`]. |
| | |
| | Attributes: |
| | title (`str`): |
| | The title of the Discussion / Pull Request |
| | status (`str`): |
| | The status of the Discussion / Pull Request. |
| | It can be one of: |
| | * `"open"` |
| | * `"closed"` |
| | * `"merged"` (only for Pull Requests ) |
| | * `"draft"` (only for Pull Requests ) |
| | num (`int`): |
| | The number of the Discussion / Pull Request. |
| | repo_id (`str`): |
| | The id (`"{namespace}/{repo_name}"`) of the repo on which |
| | the Discussion / Pull Request was open. |
| | repo_type (`str`): |
| | The type of the repo on which the Discussion / Pull Request was open. |
| | Possible values are: `"model"`, `"dataset"`, `"space"`. |
| | author (`str`): |
| | The username of the Discussion / Pull Request author. |
| | Can be `"deleted"` if the user has been deleted since. |
| | is_pull_request (`bool`): |
| | Whether or not this is a Pull Request. |
| | created_at (`datetime`): |
| | The `datetime` of creation of the Discussion / Pull Request. |
| | events (`list` of [`DiscussionEvent`]) |
| | The list of [`DiscussionEvents`] in this Discussion or Pull Request. |
| | conflicting_files (`Union[List[str], bool, None]`, *optional*): |
| | A list of conflicting files if this is a Pull Request. |
| | `None` if `self.is_pull_request` is `False`. |
| | `True` if there are conflicting files but the list can't be retrieved. |
| | target_branch (`str`, *optional*): |
| | The branch into which changes are to be merged if this is a |
| | Pull Request . `None` if `self.is_pull_request` is `False`. |
| | merge_commit_oid (`str`, *optional*): |
| | If this is a merged Pull Request , this is set to the OID / SHA of |
| | the merge commit, `None` otherwise. |
| | diff (`str`, *optional*): |
| | The git diff if this is a Pull Request , `None` otherwise. |
| | endpoint (`str`): |
| | Endpoint of the Hub. Default is https://huggingface.co. |
| | git_reference (`str`, *optional*): |
| | (property) Git reference to which changes can be pushed if this is a Pull Request, `None` otherwise. |
| | url (`str`): |
| | (property) URL of the discussion on the Hub. |
| | """ |
| |
|
| | events: List["DiscussionEvent"] |
| | conflicting_files: Union[List[str], bool, None] |
| | target_branch: Optional[str] |
| | merge_commit_oid: Optional[str] |
| | diff: Optional[str] |
| |
|
| |
|
| | @dataclass |
| | class DiscussionEvent: |
| | """ |
| | An event in a Discussion or Pull Request. |
| | |
| | Use concrete classes: |
| | * [`DiscussionComment`] |
| | * [`DiscussionStatusChange`] |
| | * [`DiscussionCommit`] |
| | * [`DiscussionTitleChange`] |
| | |
| | Attributes: |
| | id (`str`): |
| | The ID of the event. An hexadecimal string. |
| | type (`str`): |
| | The type of the event. |
| | created_at (`datetime`): |
| | A [`datetime`](https://docs.python.org/3/library/datetime.html?highlight=datetime#datetime.datetime) |
| | object holding the creation timestamp for the event. |
| | author (`str`): |
| | The username of the Discussion / Pull Request author. |
| | Can be `"deleted"` if the user has been deleted since. |
| | """ |
| |
|
| | id: str |
| | type: str |
| | created_at: datetime |
| | author: str |
| |
|
| | _event: dict |
| | """Stores the original event data, in case we need to access it later.""" |
| |
|
| |
|
| | @dataclass |
| | class DiscussionComment(DiscussionEvent): |
| | """A comment in a Discussion / Pull Request. |
| | |
| | Subclass of [`DiscussionEvent`]. |
| | |
| | |
| | Attributes: |
| | id (`str`): |
| | The ID of the event. An hexadecimal string. |
| | type (`str`): |
| | The type of the event. |
| | created_at (`datetime`): |
| | A [`datetime`](https://docs.python.org/3/library/datetime.html?highlight=datetime#datetime.datetime) |
| | object holding the creation timestamp for the event. |
| | author (`str`): |
| | The username of the Discussion / Pull Request author. |
| | Can be `"deleted"` if the user has been deleted since. |
| | content (`str`): |
| | The raw markdown content of the comment. Mentions, links and images are not rendered. |
| | edited (`bool`): |
| | Whether or not this comment has been edited. |
| | hidden (`bool`): |
| | Whether or not this comment has been hidden. |
| | """ |
| |
|
| | content: str |
| | edited: bool |
| | hidden: bool |
| |
|
| | @property |
| | def rendered(self) -> str: |
| | """The rendered comment, as a HTML string""" |
| | return self._event["data"]["latest"]["html"] |
| |
|
| | @property |
| | def last_edited_at(self) -> datetime: |
| | """The last edit time, as a `datetime` object.""" |
| | return parse_datetime(self._event["data"]["latest"]["updatedAt"]) |
| |
|
| | @property |
| | def last_edited_by(self) -> str: |
| | """The last edit time, as a `datetime` object.""" |
| | return self._event["data"]["latest"].get("author", {}).get("name", "deleted") |
| |
|
| | @property |
| | def edit_history(self) -> List[dict]: |
| | """The edit history of the comment""" |
| | return self._event["data"]["history"] |
| |
|
| | @property |
| | def number_of_edits(self) -> int: |
| | return len(self.edit_history) |
| |
|
| |
|
| | @dataclass |
| | class DiscussionStatusChange(DiscussionEvent): |
| | """A change of status in a Discussion / Pull Request. |
| | |
| | Subclass of [`DiscussionEvent`]. |
| | |
| | Attributes: |
| | id (`str`): |
| | The ID of the event. An hexadecimal string. |
| | type (`str`): |
| | The type of the event. |
| | created_at (`datetime`): |
| | A [`datetime`](https://docs.python.org/3/library/datetime.html?highlight=datetime#datetime.datetime) |
| | object holding the creation timestamp for the event. |
| | author (`str`): |
| | The username of the Discussion / Pull Request author. |
| | Can be `"deleted"` if the user has been deleted since. |
| | new_status (`str`): |
| | The status of the Discussion / Pull Request after the change. |
| | It can be one of: |
| | * `"open"` |
| | * `"closed"` |
| | * `"merged"` (only for Pull Requests ) |
| | """ |
| |
|
| | new_status: str |
| |
|
| |
|
| | @dataclass |
| | class DiscussionCommit(DiscussionEvent): |
| | """A commit in a Pull Request. |
| | |
| | Subclass of [`DiscussionEvent`]. |
| | |
| | Attributes: |
| | id (`str`): |
| | The ID of the event. An hexadecimal string. |
| | type (`str`): |
| | The type of the event. |
| | created_at (`datetime`): |
| | A [`datetime`](https://docs.python.org/3/library/datetime.html?highlight=datetime#datetime.datetime) |
| | object holding the creation timestamp for the event. |
| | author (`str`): |
| | The username of the Discussion / Pull Request author. |
| | Can be `"deleted"` if the user has been deleted since. |
| | summary (`str`): |
| | The summary of the commit. |
| | oid (`str`): |
| | The OID / SHA of the commit, as a hexadecimal string. |
| | """ |
| |
|
| | summary: str |
| | oid: str |
| |
|
| |
|
| | @dataclass |
| | class DiscussionTitleChange(DiscussionEvent): |
| | """A rename event in a Discussion / Pull Request. |
| | |
| | Subclass of [`DiscussionEvent`]. |
| | |
| | Attributes: |
| | id (`str`): |
| | The ID of the event. An hexadecimal string. |
| | type (`str`): |
| | The type of the event. |
| | created_at (`datetime`): |
| | A [`datetime`](https://docs.python.org/3/library/datetime.html?highlight=datetime#datetime.datetime) |
| | object holding the creation timestamp for the event. |
| | author (`str`): |
| | The username of the Discussion / Pull Request author. |
| | Can be `"deleted"` if the user has been deleted since. |
| | old_title (`str`): |
| | The previous title for the Discussion / Pull Request. |
| | new_title (`str`): |
| | The new title. |
| | """ |
| |
|
| | old_title: str |
| | new_title: str |
| |
|
| |
|
| | def deserialize_event(event: dict) -> DiscussionEvent: |
| | """Instantiates a [`DiscussionEvent`] from a dict""" |
| | event_id: str = event["id"] |
| | event_type: str = event["type"] |
| | created_at = parse_datetime(event["createdAt"]) |
| |
|
| | common_args = dict( |
| | id=event_id, |
| | type=event_type, |
| | created_at=created_at, |
| | author=event.get("author", {}).get("name", "deleted"), |
| | _event=event, |
| | ) |
| |
|
| | if event_type == "comment": |
| | return DiscussionComment( |
| | **common_args, |
| | edited=event["data"]["edited"], |
| | hidden=event["data"]["hidden"], |
| | content=event["data"]["latest"]["raw"], |
| | ) |
| | if event_type == "status-change": |
| | return DiscussionStatusChange( |
| | **common_args, |
| | new_status=event["data"]["status"], |
| | ) |
| | if event_type == "commit": |
| | return DiscussionCommit( |
| | **common_args, |
| | summary=event["data"]["subject"], |
| | oid=event["data"]["oid"], |
| | ) |
| | if event_type == "title-change": |
| | return DiscussionTitleChange( |
| | **common_args, |
| | old_title=event["data"]["from"], |
| | new_title=event["data"]["to"], |
| | ) |
| |
|
| | return DiscussionEvent(**common_args) |
| |
|