Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions github_rest_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""GitHub REST APIs."""

from .github import Organization, Repository, RepositoryType
from .github import Organization, Repository, RepositoryType, User

__all__ = ["Organization", "Repository", "RepositoryType"]
__all__ = ["Organization", "Repository", "RepositoryType", "User"]
65 changes: 59 additions & 6 deletions github_rest_api/github.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Simple wrapper of GitHub REST APIs."""

from abc import ABCMeta, abstractmethod
from enum import StrEnum
from typing import Any, Callable
from pathlib import Path
Expand Down Expand Up @@ -90,14 +91,13 @@ def _patch(self, url, raise_for_status: bool = True, **kwargs) -> requests.Respo
def _extract_all(
self, url: str, params: dict[str, Any] | None = None
) -> list[dict[str, Any]]:
if params is None:
params = {}
params = params.copy() if params else {}
if "per_page" not in params:
params["per_page"] = 100
params["page"] = 1
res = []
while True:
resp = self._get(url=url, params=params)
resp = self._get(url=url, params=params.copy())
resp.raise_for_status()
data = resp.json()
res.extend(data)
Expand Down Expand Up @@ -334,15 +334,22 @@ class RepositoryType(StrEnum):
PRIVATE = "private"


class Organization(GitHub):
class Owner(GitHub, metaclass=ABCMeta):
"""An abstract owner class representing an organization or user."""

def __init__(self, token: str, owner: str):
"""Initialize Repository.
:param token: An authorization token for GitHub REST APIs.
:param owner: The owner of the repository.
:param owner: The name of the owner (organization or user).
"""
super().__init__(token)
self._owner = owner
self._url_repos = f"https://api.github.com/orgs/{owner}/repos"
self._url_repos = ""
self._url_create_repo = ""

@abstractmethod
def _set_urls(self) -> None:
pass

def get_repositories(
self, type_: RepositoryType = RepositoryType.ALL
Expand All @@ -355,3 +362,49 @@ def get_repositories(

def instantiate_repository(self, repo: str) -> Repository:
return Repository(token=self._token, repo=f"{self._owner}/{repo}")

def create_repository(
self, name: str, description: str = "", private: bool = True, **kwargs
) -> dict[str, Any]:
data = {
"name": name,
"description": description,
"homepage": "https://github.com",
"private": private,
"has_issues": True,
"has_projects": True,
"has_wiki": True,
}
return self._post(url=self._url_create_repo, json=data, **kwargs).json()


class User(Owner):
"""A GitHub user."""

def __init__(self, token: str, user: str):
"""Initialize a User.
:param token: An authorization token for GitHub REST APIs.
:param user: The name of the user.
"""
super().__init__(token=token, owner=user)
self._set_urls()

def _set_urls(self) -> None:
self._url_repos = f"https://api.github.com/users/{self._owner}/repos"
self._url_create_repo = "https://api.github.com/user/repos"


class Organization(Owner):
"""A GitHub organization."""

def __init__(self, token: str, org: str):
"""Initialize an Organization.
:param token: An authorization token for GitHub REST APIs.
:param org: The name of the organization.
"""
super().__init__(token=token, owner=org)
self._set_urls()

def _set_urls(self) -> None:
self._url_repos = f"https://api.github.com/orgs/{self._owner}/repos"
self._url_create_repo = self._url_repos
40 changes: 24 additions & 16 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
[build-system]
build-backend = "hatchling.build"
requires = [ "hatchling" ]

[project]
name = "github_rest_api"
version = "0.32.0"
name = "github-rest-api"
version = "0.33.0"
description = "Simple wrapper of GitHub REST APIs."
authors = [{ name = "Ben Du", email = "longendu@yahoo.com" }]
requires-python = ">=3.11,<4"
readme = "README.md"
authors = [ { name = "Ben Du", email = "longendu@yahoo.com" } ]
requires-python = ">=3.11,<4"
classifiers = [
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]
dependencies = [
"requests>=2.28.2",
"psutil>=5.9.4",
"dulwich>=0.25.1",
"dulwich>=0.25.1",
"psutil>=5.9.4",
"requests>=2.28.2",
]

[dependency-groups]
dev = [
"deptry>=0.24.0",
"pyright>=1.1.407",
"pytest>=9.0.2",
"ruff>=0.14.10",
"ty>=0.0.8",
"deptry>=0.24",
"pyproject-fmt>=2.21.1",
"pyright>=1.1.407",
"pytest>=9.0.2",
"ruff>=0.14.10",
"ty>=0.0.8",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
17 changes: 17 additions & 0 deletions tests/test_github.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from github_rest_api.github import User, Organization


def test_user_get_repositories():
token = ""
username = "dclong"
user = User(token, username)
repos = user.get_repositories()
assert len(repos) > 0


def test_organization_get_repositories():
token = ""
org_name = "legendu-net"
org = Organization(token, org_name)
repos = org.get_repositories()
assert len(repos) > 0
36 changes: 34 additions & 2 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.