Skip to content

Commit 9b0f490

Browse files
authored
Merge pull request #25 from bellingcat/4.0/beta
4.0/beta
2 parents 86d0112 + d9e2dae commit 9b0f490

9 files changed

Lines changed: 729 additions & 190 deletions

File tree

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "octosuite"
3-
version = "4.0.0beta1"
3+
version = "4.0.0beta2"
44
description = "TUI-based toolkit for GitHub data analysis."
55
readme = "README.md"
66
license = "MIT"

‎src/octosuite/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
__pkg__ = "octosuite"
2-
__version__ = "4.0.0beta1"
2+
__version__ = "4.0.0beta2"

‎src/octosuite/core/cache.py‎

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,59 @@ class ResponseCache:
88
"""Simple in-memory cache for API responses."""
99

1010
def __init__(self):
11+
"""Initialise the ResponseCache with an empty cache dictionary."""
12+
1113
self._cache = {}
1214

1315
@staticmethod
1416
def _generate_key(url: str, params: dict = None) -> str:
15-
"""Generate a unique cache key from URL and params."""
17+
"""
18+
Generate a unique cache key from URL and parameters.
19+
20+
:param url: The URL to generate a key for.
21+
:param params: Optional dictionary of parameters to include in the key.
22+
:return: MD5 hash string representing the unique cache key.
23+
"""
24+
1625
cache_data = f"{url}:{json.dumps(params or {}, sort_keys=True)}"
1726
return hashlib.md5(cache_data.encode()).hexdigest()
1827

1928
def get(self, url: str, params: dict = None):
20-
"""Get cached response."""
29+
"""
30+
Retrieve a cached response if it exists.
31+
32+
:param url: The URL to retrieve cached data for.
33+
:param params: Optional dictionary of parameters used in the original request.
34+
:return: Cached response data if found, None otherwise.
35+
"""
36+
2137
key = self._generate_key(url, params)
2238
return self._cache.get(key)
2339

2440
def set(self, url: str, data, params: dict = None):
25-
"""Cache a response."""
41+
"""
42+
Store a response in the cache.
43+
44+
:param url: The URL to cache data for.
45+
:param data: The response data to cache.
46+
:param params: Optional dictionary of parameters used in the request.
47+
"""
48+
2649
key = self._generate_key(url, params)
2750
self._cache[key] = data
2851

2952
def clear(self):
30-
"""Clear all cached responses."""
53+
"""Clear all cached responses from memory."""
3154
self._cache.clear()
3255

3356
def remove(self, url: str, params: dict = None):
34-
"""Remove a specific cached response."""
57+
"""
58+
Remove a specific cached response.
59+
60+
:param url: The URL to remove cached data for.
61+
:param params: Optional dictionary of parameters used in the original request.
62+
"""
63+
3564
key = self._generate_key(url, params)
3665
self._cache.pop(key, None)
3766

@@ -40,5 +69,6 @@ def remove(self, url: str, params: dict = None):
4069

4170

4271
def clear_cache():
43-
"""Clear all cached API responses."""
72+
"""Clear all cached API responses from the global cache instance."""
73+
4474
cache.clear()

‎src/octosuite/core/github.py‎

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515

1616
class GitHub:
17+
"""Handles GitHub API requests with caching and response sanitisation."""
18+
1719
def __init__(
1820
self,
1921
user_agent: str = (
@@ -22,6 +24,12 @@ def __init__(
2224
f"https://github.com/bellingcat/octosuite) requests/{requests.__version__}"
2325
),
2426
):
27+
"""
28+
Initialise the GitHub API client.
29+
30+
:param user_agent: Custom User-Agent string for API requests.
31+
"""
32+
2533
self.user_agent = user_agent
2634
self.cache = cache
2735

@@ -32,6 +40,16 @@ def get(
3240
return_response: bool = False,
3341
use_cache: bool = True,
3442
) -> t.Union[dict, list, Response]:
43+
"""
44+
Make a GET request to the GitHub API.
45+
46+
:param url: The API endpoint URL.
47+
:param params: Optional query parameters for the request.
48+
:param return_response: If True, return the raw Response object instead of JSON data.
49+
:param use_cache: If True, use cached responses when available.
50+
:return: Dictionary, list, or Response object depending on the request and parameters.
51+
"""
52+
3553
if use_cache and not return_response:
3654
cached = self.cache.get(url, params)
3755
if cached is not None:
@@ -58,7 +76,14 @@ def get(
5876
def is_valid_entity(
5977
self, _type: t.Literal["user", "org", "repo"], **kwargs
6078
) -> bool:
61-
"""Validate if a GitHub entity exists."""
79+
"""
80+
Validate whether a GitHub entity exists.
81+
82+
:param _type: Type of entity to validate ("user", "org", or "repo").
83+
:param kwargs: Entity identifiers (username for user/org, repo_owner and repo_name for repo).
84+
:return: True if the entity exists, False otherwise.
85+
"""
86+
6287
try:
6388
type_map = {
6489
"user": f"https://api.github.com/users/{kwargs.get('username')}",
@@ -86,6 +111,13 @@ def is_valid_entity(
86111
return False
87112

88113
def sanitise_response(self, response: t.Union[dict, list]) -> t.Union[dict, list]:
114+
"""
115+
Remove API URLs and null values from response data recursively.
116+
117+
:param response: The response data to sanitise (dict or list).
118+
:return: Sanitised response with API URLs and null values removed.
119+
"""
120+
89121
pattern = re.compile(r"https://api\.github\.com")
90122

91123
if isinstance(response, list):

0 commit comments

Comments
 (0)