repo.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. # Copyright 2025 The HuggingFace Team. All rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Contains commands to interact with repositories on the Hugging Face Hub.
  15. Usage:
  16. # create a new dataset repo on the Hub
  17. hf repo create my-cool-dataset --repo-type=dataset
  18. # create a private model repo on the Hub
  19. hf repo create my-cool-model --private
  20. """
  21. import argparse
  22. from argparse import _SubParsersAction
  23. from typing import Optional
  24. from requests.exceptions import HTTPError
  25. from huggingface_hub.commands import BaseHuggingfaceCLICommand
  26. from huggingface_hub.commands._cli_utils import ANSI
  27. from huggingface_hub.constants import REPO_TYPES, SPACES_SDK_TYPES
  28. from huggingface_hub.errors import HfHubHTTPError, RepositoryNotFoundError, RevisionNotFoundError
  29. from huggingface_hub.hf_api import HfApi
  30. from huggingface_hub.utils import logging
  31. logger = logging.get_logger(__name__)
  32. class RepoCommands(BaseHuggingfaceCLICommand):
  33. @staticmethod
  34. def register_subcommand(parser: _SubParsersAction):
  35. repo_parser = parser.add_parser("repo", help="Manage repos on the Hub.")
  36. repo_subparsers = repo_parser.add_subparsers(help="huggingface.co repos related commands")
  37. # Show help if no subcommand is provided
  38. repo_parser.set_defaults(func=lambda args: repo_parser.print_help())
  39. # CREATE
  40. repo_create_parser = repo_subparsers.add_parser("create", help="Create a new repo on huggingface.co")
  41. repo_create_parser.add_argument(
  42. "repo_id",
  43. type=str,
  44. help="The ID of the repo to create to (e.g. `username/repo-name`). The username is optional and will be set to your username if not provided.",
  45. )
  46. repo_create_parser.add_argument(
  47. "--repo-type",
  48. type=str,
  49. help='Optional: set to "dataset" or "space" if creating a dataset or space, default is model.',
  50. )
  51. repo_create_parser.add_argument(
  52. "--space_sdk",
  53. type=str,
  54. help='Optional: Hugging Face Spaces SDK type. Required when --type is set to "space".',
  55. choices=SPACES_SDK_TYPES,
  56. )
  57. repo_create_parser.add_argument(
  58. "--private",
  59. action="store_true",
  60. help="Whether to create a private repository. Defaults to public unless the organization's default is private.",
  61. )
  62. repo_create_parser.add_argument(
  63. "--token",
  64. type=str,
  65. help="Hugging Face token. Will default to the locally saved token if not provided.",
  66. )
  67. repo_create_parser.add_argument(
  68. "--exist-ok",
  69. action="store_true",
  70. help="Do not raise an error if repo already exists.",
  71. )
  72. repo_create_parser.add_argument(
  73. "--resource-group-id",
  74. type=str,
  75. help="Resource group in which to create the repo. Resource groups is only available for Enterprise Hub organizations.",
  76. )
  77. repo_create_parser.set_defaults(func=lambda args: RepoCreateCommand(args))
  78. # TAG SUBCOMMANDS
  79. repo_tag_parser = repo_subparsers.add_parser("tag", help="Manage tags for a repo on the Hub.")
  80. tag_subparsers = repo_tag_parser.add_subparsers(help="Tag actions", dest="tag_action", required=True)
  81. # tag create
  82. tag_create_parser = tag_subparsers.add_parser("create", help="Create a tag for a repo.")
  83. tag_create_parser.add_argument(
  84. "repo_id", type=str, help="The ID of the repo to tag (e.g. `username/repo-name`)."
  85. )
  86. tag_create_parser.add_argument("tag", type=str, help="The name of the tag to create.")
  87. tag_create_parser.add_argument("-m", "--message", type=str, help="The description of the tag to create.")
  88. tag_create_parser.add_argument("--revision", type=str, help="The git revision to tag.")
  89. tag_create_parser.add_argument(
  90. "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens."
  91. )
  92. tag_create_parser.add_argument(
  93. "--repo-type",
  94. choices=["model", "dataset", "space"],
  95. default="model",
  96. help="Set the type of repository (model, dataset, or space).",
  97. )
  98. tag_create_parser.set_defaults(func=lambda args: RepoTagCreateCommand(args))
  99. # tag list
  100. tag_list_parser = tag_subparsers.add_parser("list", help="List tags for a repo.")
  101. tag_list_parser.add_argument(
  102. "repo_id", type=str, help="The ID of the repo to list tags for (e.g. `username/repo-name`)."
  103. )
  104. tag_list_parser.add_argument(
  105. "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens."
  106. )
  107. tag_list_parser.add_argument(
  108. "--repo-type",
  109. choices=["model", "dataset", "space"],
  110. default="model",
  111. help="Set the type of repository (model, dataset, or space).",
  112. )
  113. tag_list_parser.set_defaults(func=lambda args: RepoTagListCommand(args))
  114. # tag delete
  115. tag_delete_parser = tag_subparsers.add_parser("delete", help="Delete a tag from a repo.")
  116. tag_delete_parser.add_argument(
  117. "repo_id", type=str, help="The ID of the repo to delete the tag from (e.g. `username/repo-name`)."
  118. )
  119. tag_delete_parser.add_argument("tag", type=str, help="The name of the tag to delete.")
  120. tag_delete_parser.add_argument("-y", "--yes", action="store_true", help="Answer Yes to prompts automatically.")
  121. tag_delete_parser.add_argument(
  122. "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens."
  123. )
  124. tag_delete_parser.add_argument(
  125. "--repo-type",
  126. choices=["model", "dataset", "space"],
  127. default="model",
  128. help="Set the type of repository (model, dataset, or space).",
  129. )
  130. tag_delete_parser.set_defaults(func=lambda args: RepoTagDeleteCommand(args))
  131. class RepoCreateCommand:
  132. def __init__(self, args: argparse.Namespace):
  133. self.repo_id: str = args.repo_id
  134. self.repo_type: Optional[str] = args.repo_type
  135. self.space_sdk: Optional[str] = args.space_sdk
  136. self.private: bool = args.private
  137. self.token: Optional[str] = args.token
  138. self.exist_ok: bool = args.exist_ok
  139. self.resource_group_id: Optional[str] = args.resource_group_id
  140. self._api = HfApi()
  141. def run(self):
  142. repo_url = self._api.create_repo(
  143. repo_id=self.repo_id,
  144. repo_type=self.repo_type,
  145. private=self.private,
  146. token=self.token,
  147. exist_ok=self.exist_ok,
  148. resource_group_id=self.resource_group_id,
  149. space_sdk=self.space_sdk,
  150. )
  151. print(f"Successfully created {ANSI.bold(repo_url.repo_id)} on the Hub.")
  152. print(f"Your repo is now available at {ANSI.bold(repo_url)}")
  153. class RepoTagCommand:
  154. def __init__(self, args):
  155. self.args = args
  156. self.api = HfApi(token=getattr(args, "token", None))
  157. self.repo_id = args.repo_id
  158. self.repo_type = getattr(args, "repo_type", "model")
  159. if self.repo_type not in REPO_TYPES:
  160. print("Invalid repo --repo-type")
  161. exit(1)
  162. class RepoTagCreateCommand(RepoTagCommand):
  163. def run(self):
  164. print(
  165. f"You are about to create tag {ANSI.bold(str(self.args.tag))} on {self.repo_type} {ANSI.bold(self.repo_id)}"
  166. )
  167. try:
  168. self.api.create_tag(
  169. repo_id=self.repo_id,
  170. tag=self.args.tag,
  171. tag_message=getattr(self.args, "message", None),
  172. revision=getattr(self.args, "revision", None),
  173. repo_type=self.repo_type,
  174. )
  175. except RepositoryNotFoundError:
  176. print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.")
  177. exit(1)
  178. except RevisionNotFoundError:
  179. print(f"Revision {ANSI.bold(str(getattr(self.args, 'revision', None)))} not found.")
  180. exit(1)
  181. except HfHubHTTPError as e:
  182. if e.response.status_code == 409:
  183. print(f"Tag {ANSI.bold(str(self.args.tag))} already exists on {ANSI.bold(self.repo_id)}")
  184. exit(1)
  185. raise e
  186. print(f"Tag {ANSI.bold(str(self.args.tag))} created on {ANSI.bold(self.repo_id)}")
  187. class RepoTagListCommand(RepoTagCommand):
  188. def run(self):
  189. try:
  190. refs = self.api.list_repo_refs(
  191. repo_id=self.repo_id,
  192. repo_type=self.repo_type,
  193. )
  194. except RepositoryNotFoundError:
  195. print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.")
  196. exit(1)
  197. except HTTPError as e:
  198. print(e)
  199. print(ANSI.red(e.response.text))
  200. exit(1)
  201. if len(refs.tags) == 0:
  202. print("No tags found")
  203. exit(0)
  204. print(f"Tags for {self.repo_type} {ANSI.bold(self.repo_id)}:")
  205. for tag in refs.tags:
  206. print(tag.name)
  207. class RepoTagDeleteCommand(RepoTagCommand):
  208. def run(self):
  209. print(f"You are about to delete tag {ANSI.bold(self.args.tag)} on {self.repo_type} {ANSI.bold(self.repo_id)}")
  210. if not getattr(self.args, "yes", False):
  211. choice = input("Proceed? [Y/n] ").lower()
  212. if choice not in ("", "y", "yes"):
  213. print("Abort")
  214. exit()
  215. try:
  216. self.api.delete_tag(repo_id=self.repo_id, tag=self.args.tag, repo_type=self.repo_type)
  217. except RepositoryNotFoundError:
  218. print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.")
  219. exit(1)
  220. except RevisionNotFoundError:
  221. print(f"Tag {ANSI.bold(self.args.tag)} not found on {ANSI.bold(self.repo_id)}")
  222. exit(1)
  223. print(f"Tag {ANSI.bold(self.args.tag)} deleted on {ANSI.bold(self.repo_id)}")