gitstar 1.0.0__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
gitstar-1.1.0/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Imran Pollob
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
gitstar-1.1.0/PKG-INFO ADDED
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: gitstar
3
+ Version: 1.1.0
4
+ Summary: Calculate the total stars earned by a GitHub user or organization
5
+ Author-email: Imran Pollob <imranpollob@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/imranpollob/github-star-calculator
8
+ Project-URL: Repository, https://github.com/imranpollob/github-star-calculator.git
9
+ Project-URL: Issues, https://github.com/imranpollob/github-star-calculator/issues
10
+ Keywords: github,stars,stargazers,cli,github-api,developer-tools
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Software Development :: Version Control :: Git
24
+ Classifier: Topic :: Utilities
25
+ Requires-Python: >=3.8
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Dynamic: license-file
29
+
30
+ # Gitstar
31
+
32
+ [![PyPI version](https://img.shields.io/pypi/v/gitstar.svg)](https://pypi.org/project/gitstar/)
33
+ [![Python versions](https://img.shields.io/pypi/pyversions/gitstar.svg)](https://pypi.org/project/gitstar/)
34
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
35
+
36
+ A fast, lightweight, zero-dependency command-line tool to calculate the total number of stars earned by any GitHub user or organization.
37
+
38
+ ---
39
+
40
+ ## Features
41
+
42
+ - **Fast & Zero Dependencies**: Built entirely with Python's standard library. No external runtime dependencies.
43
+ - **Cross-Platform**: Full support for Linux, macOS, and Windows (with safe terminal encoding fallbacks).
44
+ - **Authentication & Rate-Limit Friendly**: Supports GitHub Personal Access Tokens (`--token` or `GITHUB_TOKEN` / `GH_TOKEN` environment variables) to bypass the unauthenticated 60 requests/hour limit.
45
+ - **Informative Output**: Clean tabular layout ranking repositories by stars.
46
+
47
+ ---
48
+
49
+ ## Installation
50
+
51
+ Install via pip:
52
+
53
+ ```bash
54
+ pip install gitstar
55
+ ```
56
+
57
+ Or upgrade to the latest version:
58
+
59
+ ```bash
60
+ pip install --upgrade gitstar
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Usage
66
+
67
+ ### Basic Usage
68
+
69
+ Find the total stars for any GitHub user or organization:
70
+
71
+ ```bash
72
+ gitstar <username>
73
+ ```
74
+
75
+ Example:
76
+
77
+ ```bash
78
+ gitstar torvalds
79
+ ```
80
+
81
+ ### Limiting Results
82
+
83
+ Display only the top $N$ starred repositories:
84
+
85
+ ```bash
86
+ # Using positional shorthand:
87
+ gitstar torvalds 5
88
+
89
+ # Or using the -l / --limit flag:
90
+ gitstar torvalds --limit 5
91
+ ```
92
+
93
+ ### Output Example
94
+
95
+ ```text
96
+ Total ⭐️ 234120
97
+ https://github.com/torvalds/linux ⭐️ 186400
98
+ https://github.com/torvalds/subsurface ⭐️ 3150
99
+ https://github.com/torvalds/pesconvert ⭐️ 285
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Authentication & Rate Limits
105
+
106
+ By default, unauthenticated GitHub API requests are limited to **60 requests per hour** per IP address.
107
+
108
+ To increase your limit to **5,000 requests per hour**, provide a GitHub Personal Access Token using either:
109
+
110
+ 1. **Environment Variable** (recommended):
111
+ ```bash
112
+ export GITHUB_TOKEN="ghp_your_token_here"
113
+ gitstar torvalds
114
+ ```
115
+ *(On Windows PowerShell: `$env:GITHUB_TOKEN="ghp_your_token_here"`)*
116
+
117
+ 2. **CLI Option**:
118
+ ```bash
119
+ gitstar torvalds --token ghp_your_token_here
120
+ ```
121
+
122
+ ---
123
+
124
+ ## CLI Options Reference
125
+
126
+ ```text
127
+ usage: gitstar [-h] [-l LIMIT] [-t TOKEN] [-v] [username] [limit]
128
+
129
+ Calculate the total stars earned by a GitHub user or organization.
130
+
131
+ positional arguments:
132
+ username GitHub username or organization
133
+ limit Number of top repositories to display (positional shorthand)
134
+
135
+ options:
136
+ -h, --help show this help message and exit
137
+ -l, --limit LIMIT Number of top repositories to display
138
+ -t, --token TOKEN GitHub Personal Access Token (defaults to GITHUB_TOKEN or GH_TOKEN)
139
+ -v, --version show program's version number and exit
140
+ ```
141
+
142
+ ---
143
+
144
+ ## License
145
+
146
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,117 @@
1
+ # Gitstar
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/gitstar.svg)](https://pypi.org/project/gitstar/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/gitstar.svg)](https://pypi.org/project/gitstar/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+
7
+ A fast, lightweight, zero-dependency command-line tool to calculate the total number of stars earned by any GitHub user or organization.
8
+
9
+ ---
10
+
11
+ ## Features
12
+
13
+ - **Fast & Zero Dependencies**: Built entirely with Python's standard library. No external runtime dependencies.
14
+ - **Cross-Platform**: Full support for Linux, macOS, and Windows (with safe terminal encoding fallbacks).
15
+ - **Authentication & Rate-Limit Friendly**: Supports GitHub Personal Access Tokens (`--token` or `GITHUB_TOKEN` / `GH_TOKEN` environment variables) to bypass the unauthenticated 60 requests/hour limit.
16
+ - **Informative Output**: Clean tabular layout ranking repositories by stars.
17
+
18
+ ---
19
+
20
+ ## Installation
21
+
22
+ Install via pip:
23
+
24
+ ```bash
25
+ pip install gitstar
26
+ ```
27
+
28
+ Or upgrade to the latest version:
29
+
30
+ ```bash
31
+ pip install --upgrade gitstar
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Usage
37
+
38
+ ### Basic Usage
39
+
40
+ Find the total stars for any GitHub user or organization:
41
+
42
+ ```bash
43
+ gitstar <username>
44
+ ```
45
+
46
+ Example:
47
+
48
+ ```bash
49
+ gitstar torvalds
50
+ ```
51
+
52
+ ### Limiting Results
53
+
54
+ Display only the top $N$ starred repositories:
55
+
56
+ ```bash
57
+ # Using positional shorthand:
58
+ gitstar torvalds 5
59
+
60
+ # Or using the -l / --limit flag:
61
+ gitstar torvalds --limit 5
62
+ ```
63
+
64
+ ### Output Example
65
+
66
+ ```text
67
+ Total ⭐️ 234120
68
+ https://github.com/torvalds/linux ⭐️ 186400
69
+ https://github.com/torvalds/subsurface ⭐️ 3150
70
+ https://github.com/torvalds/pesconvert ⭐️ 285
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Authentication & Rate Limits
76
+
77
+ By default, unauthenticated GitHub API requests are limited to **60 requests per hour** per IP address.
78
+
79
+ To increase your limit to **5,000 requests per hour**, provide a GitHub Personal Access Token using either:
80
+
81
+ 1. **Environment Variable** (recommended):
82
+ ```bash
83
+ export GITHUB_TOKEN="ghp_your_token_here"
84
+ gitstar torvalds
85
+ ```
86
+ *(On Windows PowerShell: `$env:GITHUB_TOKEN="ghp_your_token_here"`)*
87
+
88
+ 2. **CLI Option**:
89
+ ```bash
90
+ gitstar torvalds --token ghp_your_token_here
91
+ ```
92
+
93
+ ---
94
+
95
+ ## CLI Options Reference
96
+
97
+ ```text
98
+ usage: gitstar [-h] [-l LIMIT] [-t TOKEN] [-v] [username] [limit]
99
+
100
+ Calculate the total stars earned by a GitHub user or organization.
101
+
102
+ positional arguments:
103
+ username GitHub username or organization
104
+ limit Number of top repositories to display (positional shorthand)
105
+
106
+ options:
107
+ -h, --help show this help message and exit
108
+ -l, --limit LIMIT Number of top repositories to display
109
+ -t, --token TOKEN GitHub Personal Access Token (defaults to GITHUB_TOKEN or GH_TOKEN)
110
+ -v, --version show program's version number and exit
111
+ ```
112
+
113
+ ---
114
+
115
+ ## License
116
+
117
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,7 @@
1
+ """gitstar - Calculate the total stars earned by a GitHub user or organization."""
2
+
3
+ __version__ = "1.1.0"
4
+
5
+ from .core import main
6
+
7
+ __all__ = ["main", "__version__"]
@@ -0,0 +1,5 @@
1
+ from .core import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
5
+
@@ -0,0 +1,217 @@
1
+ import argparse
2
+ import datetime
3
+ import json
4
+ import math
5
+ import os
6
+ import sys
7
+ from urllib.error import HTTPError, URLError
8
+ from urllib.parse import quote
9
+ from urllib.request import Request, urlopen
10
+
11
+ VERSION = "1.1.0"
12
+
13
+
14
+ def configure_terminal_encoding():
15
+ """Ensure standard output streams handle UTF-8 without crashing on Windows."""
16
+ if sys.platform == "win32":
17
+ for stream in (sys.stdout, sys.stderr):
18
+ if hasattr(stream, "reconfigure"):
19
+ try:
20
+ stream.reconfigure(encoding="utf-8", errors="replace")
21
+ except Exception:
22
+ pass
23
+
24
+
25
+ def get_star_symbol() -> str:
26
+ """Return a star symbol compatible with the current terminal encoding."""
27
+ for symbol in ("⭐️", "★", "*"):
28
+ try:
29
+ encoding = getattr(sys.stdout, "encoding", None) or "utf-8"
30
+ symbol.encode(encoding)
31
+ return symbol
32
+ except (UnicodeEncodeError, LookupError):
33
+ continue
34
+ return "*"
35
+
36
+
37
+ def fetch(endpoint: str, token: str = None):
38
+ """Fetch JSON from GitHub API with proper headers, timeout, and error handling."""
39
+ url = f"https://api.github.com/{endpoint.lstrip('/')}"
40
+ headers = {
41
+ "Accept": "application/vnd.github.v3+json",
42
+ "User-Agent": f"gitstar/{VERSION}",
43
+ }
44
+ if token:
45
+ headers["Authorization"] = f"Bearer {token}"
46
+
47
+ request = Request(url, headers=headers)
48
+
49
+ try:
50
+ with urlopen(request, timeout=15) as response:
51
+ return json.loads(response.read().decode("utf-8"))
52
+ except HTTPError as e:
53
+ try:
54
+ e.close()
55
+ except Exception:
56
+ pass
57
+ if e.code == 404:
58
+ print(f"Error: GitHub user or organization not found.", file=sys.stderr)
59
+ elif e.code == 403:
60
+ reset_time = e.headers.get("x-ratelimit-reset")
61
+ if reset_time:
62
+ try:
63
+ reset_dt = datetime.datetime.fromtimestamp(
64
+ int(reset_time), tz=datetime.timezone.utc
65
+ )
66
+ reset_str = reset_dt.strftime("%Y-%m-%d %H:%M:%S UTC")
67
+ except Exception:
68
+ reset_str = reset_time
69
+ print(
70
+ f"Error: GitHub API rate limit exceeded.\n"
71
+ f"Rate limit resets at: {reset_str}\n"
72
+ f"Tip: Provide a GitHub token using --token or the GITHUB_TOKEN environment variable.",
73
+ file=sys.stderr,
74
+ )
75
+ else:
76
+ print(
77
+ f"Error: Access forbidden (HTTP 403).\n"
78
+ f"Reason: {e.reason}\n"
79
+ f"Tip: If you hit the rate limit, provide a GitHub token using --token or GITHUB_TOKEN.",
80
+ file=sys.stderr,
81
+ )
82
+ elif e.code == 401:
83
+ print(
84
+ "Error: Unauthorized (HTTP 401). Please check your GitHub token.",
85
+ file=sys.stderr,
86
+ )
87
+ else:
88
+ print(f"Error: GitHub API request failed ({e.code} {e.reason}).", file=sys.stderr)
89
+ sys.exit(1)
90
+ except URLError as e:
91
+ print(
92
+ f"Error: Network connection failed ({e.reason}). Please check your internet connection.",
93
+ file=sys.stderr,
94
+ )
95
+ sys.exit(1)
96
+ except TimeoutError:
97
+ print("Error: Request timed out while connecting to GitHub.", file=sys.stderr)
98
+ sys.exit(1)
99
+
100
+
101
+ def build_parser() -> argparse.ArgumentParser:
102
+ """Build command-line argument parser."""
103
+ parser = argparse.ArgumentParser(
104
+ prog="gitstar",
105
+ description="Calculate the total stars earned by a GitHub user or organization.",
106
+ )
107
+ parser.add_argument(
108
+ "username",
109
+ nargs="?",
110
+ default=None,
111
+ help="GitHub username or organization",
112
+ )
113
+ parser.add_argument(
114
+ "pos_limit",
115
+ nargs="?",
116
+ type=int,
117
+ default=None,
118
+ metavar="limit",
119
+ help="Number of top repositories to display (positional shorthand)",
120
+ )
121
+ parser.add_argument(
122
+ "-l",
123
+ "--limit",
124
+ type=int,
125
+ default=None,
126
+ help="Number of top repositories to display",
127
+ )
128
+ parser.add_argument(
129
+ "-t",
130
+ "--token",
131
+ type=str,
132
+ default=None,
133
+ help="GitHub Personal Access Token (defaults to GITHUB_TOKEN or GH_TOKEN environment variable)",
134
+ )
135
+ parser.add_argument(
136
+ "-v",
137
+ "--version",
138
+ action="version",
139
+ version=f"%(prog)s {VERSION}",
140
+ )
141
+ return parser
142
+
143
+
144
+ def main(argv=None):
145
+ configure_terminal_encoding()
146
+ parser = build_parser()
147
+
148
+ if argv is None:
149
+ argv = sys.argv[1:]
150
+
151
+ # Show friendly help when invoked with no arguments
152
+ if len(argv) == 0:
153
+ parser.print_help()
154
+ sys.exit(0)
155
+
156
+ args = parser.parse_args(argv)
157
+
158
+ if not args.username:
159
+ parser.print_help()
160
+ sys.exit(0)
161
+
162
+ # Determine limit (flag has priority over positional shorthand)
163
+ limit = args.limit if args.limit is not None else args.pos_limit
164
+ if limit is not None and limit < 0:
165
+ parser.error("Limit must be a non-negative integer.")
166
+
167
+ # Determine token: CLI flag -> GITHUB_TOKEN -> GH_TOKEN
168
+ token = (
169
+ args.token
170
+ or os.environ.get("GITHUB_TOKEN")
171
+ or os.environ.get("GH_TOKEN")
172
+ )
173
+
174
+ username = args.username.strip()
175
+ user = fetch(f"users/{quote(username)}", token=token)
176
+
177
+ public_repos = user.get("public_repos", 0)
178
+ if not public_repos:
179
+ print(f"'{username}' does not have any public repositories.")
180
+ sys.exit(0)
181
+
182
+ pages = math.ceil(public_repos / 100)
183
+ total_stars = 0
184
+ repo_star_map = {}
185
+ highest_name = 0
186
+
187
+ for page in range(1, pages + 1):
188
+ repos = fetch(
189
+ f"users/{quote(username)}/repos?per_page=100&page={page}",
190
+ token=token,
191
+ )
192
+ if not repos:
193
+ break
194
+
195
+ for repo in repos:
196
+ repo_url = repo.get("html_url", repo.get("name", "unknown"))
197
+ stars = repo.get("stargazers_count", 0)
198
+ repo_star_map[repo_url] = stars
199
+ total_stars += stars
200
+ highest_name = max(highest_name, len(repo_url))
201
+
202
+ star_symbol = get_star_symbol()
203
+ print(f"Total {star_symbol} {total_stars}")
204
+
205
+ if limit != 0 and repo_star_map:
206
+ sorted_repos = sorted(
207
+ repo_star_map.items(), key=lambda x: x[1], reverse=True
208
+ )
209
+ if limit is not None:
210
+ sorted_repos = sorted_repos[:limit]
211
+
212
+ for repo, stars in sorted_repos:
213
+ print(f"{repo:<{highest_name}} {star_symbol} {stars}")
214
+
215
+
216
+ if __name__ == "__main__":
217
+ main()
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: gitstar
3
+ Version: 1.1.0
4
+ Summary: Calculate the total stars earned by a GitHub user or organization
5
+ Author-email: Imran Pollob <imranpollob@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/imranpollob/github-star-calculator
8
+ Project-URL: Repository, https://github.com/imranpollob/github-star-calculator.git
9
+ Project-URL: Issues, https://github.com/imranpollob/github-star-calculator/issues
10
+ Keywords: github,stars,stargazers,cli,github-api,developer-tools
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Programming Language :: Python :: 3.14
23
+ Classifier: Topic :: Software Development :: Version Control :: Git
24
+ Classifier: Topic :: Utilities
25
+ Requires-Python: >=3.8
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Dynamic: license-file
29
+
30
+ # Gitstar
31
+
32
+ [![PyPI version](https://img.shields.io/pypi/v/gitstar.svg)](https://pypi.org/project/gitstar/)
33
+ [![Python versions](https://img.shields.io/pypi/pyversions/gitstar.svg)](https://pypi.org/project/gitstar/)
34
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
35
+
36
+ A fast, lightweight, zero-dependency command-line tool to calculate the total number of stars earned by any GitHub user or organization.
37
+
38
+ ---
39
+
40
+ ## Features
41
+
42
+ - **Fast & Zero Dependencies**: Built entirely with Python's standard library. No external runtime dependencies.
43
+ - **Cross-Platform**: Full support for Linux, macOS, and Windows (with safe terminal encoding fallbacks).
44
+ - **Authentication & Rate-Limit Friendly**: Supports GitHub Personal Access Tokens (`--token` or `GITHUB_TOKEN` / `GH_TOKEN` environment variables) to bypass the unauthenticated 60 requests/hour limit.
45
+ - **Informative Output**: Clean tabular layout ranking repositories by stars.
46
+
47
+ ---
48
+
49
+ ## Installation
50
+
51
+ Install via pip:
52
+
53
+ ```bash
54
+ pip install gitstar
55
+ ```
56
+
57
+ Or upgrade to the latest version:
58
+
59
+ ```bash
60
+ pip install --upgrade gitstar
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Usage
66
+
67
+ ### Basic Usage
68
+
69
+ Find the total stars for any GitHub user or organization:
70
+
71
+ ```bash
72
+ gitstar <username>
73
+ ```
74
+
75
+ Example:
76
+
77
+ ```bash
78
+ gitstar torvalds
79
+ ```
80
+
81
+ ### Limiting Results
82
+
83
+ Display only the top $N$ starred repositories:
84
+
85
+ ```bash
86
+ # Using positional shorthand:
87
+ gitstar torvalds 5
88
+
89
+ # Or using the -l / --limit flag:
90
+ gitstar torvalds --limit 5
91
+ ```
92
+
93
+ ### Output Example
94
+
95
+ ```text
96
+ Total ⭐️ 234120
97
+ https://github.com/torvalds/linux ⭐️ 186400
98
+ https://github.com/torvalds/subsurface ⭐️ 3150
99
+ https://github.com/torvalds/pesconvert ⭐️ 285
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Authentication & Rate Limits
105
+
106
+ By default, unauthenticated GitHub API requests are limited to **60 requests per hour** per IP address.
107
+
108
+ To increase your limit to **5,000 requests per hour**, provide a GitHub Personal Access Token using either:
109
+
110
+ 1. **Environment Variable** (recommended):
111
+ ```bash
112
+ export GITHUB_TOKEN="ghp_your_token_here"
113
+ gitstar torvalds
114
+ ```
115
+ *(On Windows PowerShell: `$env:GITHUB_TOKEN="ghp_your_token_here"`)*
116
+
117
+ 2. **CLI Option**:
118
+ ```bash
119
+ gitstar torvalds --token ghp_your_token_here
120
+ ```
121
+
122
+ ---
123
+
124
+ ## CLI Options Reference
125
+
126
+ ```text
127
+ usage: gitstar [-h] [-l LIMIT] [-t TOKEN] [-v] [username] [limit]
128
+
129
+ Calculate the total stars earned by a GitHub user or organization.
130
+
131
+ positional arguments:
132
+ username GitHub username or organization
133
+ limit Number of top repositories to display (positional shorthand)
134
+
135
+ options:
136
+ -h, --help show this help message and exit
137
+ -l, --limit LIMIT Number of top repositories to display
138
+ -t, --token TOKEN GitHub Personal Access Token (defaults to GITHUB_TOKEN or GH_TOKEN)
139
+ -v, --version show program's version number and exit
140
+ ```
141
+
142
+ ---
143
+
144
+ ## License
145
+
146
+ This project is licensed under the [MIT License](LICENSE).
@@ -1,10 +1,13 @@
1
+ LICENSE
1
2
  README.md
2
3
  pyproject.toml
3
4
  setup.py
4
5
  gitstar/__init__.py
6
+ gitstar/__main__.py
5
7
  gitstar/core.py
6
8
  gitstar.egg-info/PKG-INFO
7
9
  gitstar.egg-info/SOURCES.txt
8
10
  gitstar.egg-info/dependency_links.txt
9
11
  gitstar.egg-info/entry_points.txt
10
- gitstar.egg-info/top_level.txt
12
+ gitstar.egg-info/top_level.txt
13
+ tests/test_cli.py
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "gitstar"
7
+ version = "1.1.0"
8
+ description = "Calculate the total stars earned by a GitHub user or organization"
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ authors = [
12
+ { name = "Imran Pollob", email = "imranpollob@gmail.com" }
13
+ ]
14
+ requires-python = ">=3.8"
15
+ keywords = ["github", "stars", "stargazers", "cli", "github-api", "developer-tools"]
16
+ classifiers = [
17
+ "Development Status :: 5 - Production/Stable",
18
+ "Environment :: Console",
19
+ "Intended Audience :: Developers",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.8",
23
+ "Programming Language :: Python :: 3.9",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Programming Language :: Python :: 3.13",
28
+ "Programming Language :: Python :: 3.14",
29
+ "Topic :: Software Development :: Version Control :: Git",
30
+ "Topic :: Utilities",
31
+ ]
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/imranpollob/github-star-calculator"
35
+ Repository = "https://github.com/imranpollob/github-star-calculator.git"
36
+ Issues = "https://github.com/imranpollob/github-star-calculator/issues"
37
+
38
+ [project.scripts]
39
+ gitstar = "gitstar.core:main"
40
+
41
+ [tool.setuptools.packages.find]
42
+ where = ["."]
43
+ include = ["gitstar*"]
@@ -1,4 +1,4 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
gitstar-1.1.0/setup.py ADDED
@@ -0,0 +1,3 @@
1
+ from setuptools import setup
2
+
3
+ setup()
@@ -0,0 +1,157 @@
1
+ import io
2
+ import sys
3
+ import unittest
4
+ from unittest.mock import MagicMock, patch
5
+ from urllib.error import HTTPError, URLError
6
+
7
+ from gitstar.core import (
8
+ VERSION,
9
+ build_parser,
10
+ fetch,
11
+ get_star_symbol,
12
+ main,
13
+ )
14
+
15
+
16
+ class TestGitstarCLI(unittest.TestCase):
17
+ def test_parser_basic(self):
18
+ parser = build_parser()
19
+ args = parser.parse_args(["octocat"])
20
+ self.assertEqual(args.username, "octocat")
21
+ self.assertIsNone(args.pos_limit)
22
+ self.assertIsNone(args.limit)
23
+ self.assertIsNone(args.token)
24
+
25
+ def test_parser_positional_limit(self):
26
+ parser = build_parser()
27
+ args = parser.parse_args(["octocat", "5"])
28
+ self.assertEqual(args.username, "octocat")
29
+ self.assertEqual(args.pos_limit, 5)
30
+
31
+ def test_parser_flag_limit(self):
32
+ parser = build_parser()
33
+ args = parser.parse_args(["octocat", "-l", "10"])
34
+ self.assertEqual(args.username, "octocat")
35
+ self.assertEqual(args.limit, 10)
36
+
37
+ def test_parser_token(self):
38
+ parser = build_parser()
39
+ args = parser.parse_args(["octocat", "--token", "ghp_secret"])
40
+ self.assertEqual(args.token, "ghp_secret")
41
+
42
+ def test_get_star_symbol(self):
43
+ symbol = get_star_symbol()
44
+ self.assertIn(symbol, ("⭐️", "★", "*"))
45
+
46
+ def test_main_no_args_shows_help(self):
47
+ with patch("sys.stdout", new_callable=io.StringIO) as mock_stdout:
48
+ with self.assertRaises(SystemExit) as cm:
49
+ main([])
50
+ self.assertEqual(cm.exception.code, 0)
51
+ self.assertIn("Calculate the total stars", mock_stdout.getvalue())
52
+
53
+ @patch("gitstar.core.fetch")
54
+ def test_main_success(self, mock_fetch):
55
+ # Mock user info and repos
56
+ mock_fetch.side_effect = [
57
+ {"public_repos": 2},
58
+ [
59
+ {"html_url": "https://github.com/test/repo1", "stargazers_count": 25},
60
+ {"html_url": "https://github.com/test/repo2", "stargazers_count": 10},
61
+ ],
62
+ ]
63
+
64
+ with patch("sys.stdout", new_callable=io.StringIO) as mock_stdout:
65
+ main(["testuser"])
66
+ output = mock_stdout.getvalue()
67
+ self.assertIn("Total", output)
68
+ self.assertIn("35", output)
69
+ self.assertIn("https://github.com/test/repo1", output)
70
+ self.assertIn("25", output)
71
+ self.assertIn("https://github.com/test/repo2", output)
72
+ self.assertIn("10", output)
73
+
74
+ @patch("gitstar.core.fetch")
75
+ def test_main_with_limit(self, mock_fetch):
76
+ mock_fetch.side_effect = [
77
+ {"public_repos": 3},
78
+ [
79
+ {"html_url": "https://github.com/test/repo1", "stargazers_count": 100},
80
+ {"html_url": "https://github.com/test/repo2", "stargazers_count": 50},
81
+ {"html_url": "https://github.com/test/repo3", "stargazers_count": 10},
82
+ ],
83
+ ]
84
+
85
+ with patch("sys.stdout", new_callable=io.StringIO) as mock_stdout:
86
+ main(["testuser", "1"])
87
+ output = mock_stdout.getvalue()
88
+ self.assertIn("Total", output)
89
+ self.assertIn("160", output)
90
+ self.assertIn("https://github.com/test/repo1", output)
91
+ self.assertNotIn("https://github.com/test/repo2", output)
92
+ self.assertNotIn("https://github.com/test/repo3", output)
93
+
94
+ @patch("gitstar.core.fetch")
95
+ def test_main_no_public_repos(self, mock_fetch):
96
+ mock_fetch.return_value = {"public_repos": 0}
97
+
98
+ with patch("sys.stdout", new_callable=io.StringIO) as mock_stdout:
99
+ with self.assertRaises(SystemExit) as cm:
100
+ main(["emptyuser"])
101
+ self.assertEqual(cm.exception.code, 0)
102
+ self.assertIn("does not have any public repositories", mock_stdout.getvalue())
103
+
104
+ @patch("gitstar.core.urlopen")
105
+ def test_fetch_user_not_found(self, mock_urlopen):
106
+ err = HTTPError(
107
+ url="https://api.github.com/users/nonexistent",
108
+ code=404,
109
+ msg="Not Found",
110
+ hdrs={},
111
+ fp=io.BytesIO(b"{}"),
112
+ )
113
+ mock_urlopen.side_effect = err
114
+
115
+ with patch("sys.stderr", new_callable=io.StringIO) as mock_stderr:
116
+ with self.assertRaises(SystemExit) as cm:
117
+ fetch("users/nonexistent")
118
+ self.assertEqual(cm.exception.code, 1)
119
+ self.assertIn("not found", mock_stderr.getvalue())
120
+
121
+ @patch("gitstar.core.urlopen")
122
+ def test_fetch_rate_limit_exceeded(self, mock_urlopen):
123
+ headers = {
124
+ "x-ratelimit-remaining": "0",
125
+ "x-ratelimit-reset": "1700000000",
126
+ }
127
+ err = HTTPError(
128
+ url="https://api.github.com/users/ratelimited",
129
+ code=403,
130
+ msg="Forbidden",
131
+ hdrs=headers,
132
+ fp=io.BytesIO(b"{}"),
133
+ )
134
+ mock_urlopen.side_effect = err
135
+
136
+ with patch("sys.stderr", new_callable=io.StringIO) as mock_stderr:
137
+ with self.assertRaises(SystemExit) as cm:
138
+ fetch("users/ratelimited")
139
+ self.assertEqual(cm.exception.code, 1)
140
+ stderr_val = mock_stderr.getvalue()
141
+ self.assertIn("rate limit exceeded", stderr_val)
142
+ self.assertIn("Tip:", stderr_val)
143
+
144
+ @patch("gitstar.core.urlopen")
145
+ def test_fetch_network_error(self, mock_urlopen):
146
+ mock_urlopen.side_effect = URLError("DNS lookup failed")
147
+
148
+ with patch("sys.stderr", new_callable=io.StringIO) as mock_stderr:
149
+ with self.assertRaises(SystemExit) as cm:
150
+ fetch("users/test")
151
+ self.assertEqual(cm.exception.code, 1)
152
+ self.assertIn("Network connection failed", mock_stderr.getvalue())
153
+
154
+
155
+ if __name__ == "__main__":
156
+ unittest.main()
157
+
gitstar-1.0.0/PKG-INFO DELETED
@@ -1,60 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: gitstar
3
- Version: 1.0.0
4
- Summary: Find out the total star earned by a github user
5
- Home-page: https://github.com/imranpollob/gitstar
6
- Author: Imran Pollob
7
- Author-email: imranpollob@gmail.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.6
12
- Description-Content-Type: text/markdown
13
- Dynamic: author
14
- Dynamic: author-email
15
- Dynamic: classifier
16
- Dynamic: description
17
- Dynamic: description-content-type
18
- Dynamic: home-page
19
- Dynamic: requires-python
20
- Dynamic: summary
21
-
22
- # Gitstar
23
-
24
- A command-line tool to calculate the total number of stars earned by a GitHub user.
25
-
26
- ## Installation
27
-
28
- Install via pip:
29
-
30
- ```bash
31
- pip install gitstar
32
- ```
33
-
34
- ## Usage
35
-
36
- Basic usage:
37
-
38
- ```bash
39
- gitstar <username>
40
- ```
41
-
42
- Limit the output to the top N repositories:
43
-
44
- ```bash
45
- gitstar <username> <limit>
46
- ```
47
-
48
- ### Examples
49
-
50
- Get total stars for user `pollmix`:
51
-
52
- ```bash
53
- gitstar pollmix
54
- ```
55
-
56
- Get total stars and list top 5 repositories:
57
-
58
- ```bash
59
- gitstar pollmix 5
60
- ```
gitstar-1.0.0/README.md DELETED
@@ -1,39 +0,0 @@
1
- # Gitstar
2
-
3
- A command-line tool to calculate the total number of stars earned by a GitHub user.
4
-
5
- ## Installation
6
-
7
- Install via pip:
8
-
9
- ```bash
10
- pip install gitstar
11
- ```
12
-
13
- ## Usage
14
-
15
- Basic usage:
16
-
17
- ```bash
18
- gitstar <username>
19
- ```
20
-
21
- Limit the output to the top N repositories:
22
-
23
- ```bash
24
- gitstar <username> <limit>
25
- ```
26
-
27
- ### Examples
28
-
29
- Get total stars for user `pollmix`:
30
-
31
- ```bash
32
- gitstar pollmix
33
- ```
34
-
35
- Get total stars and list top 5 repositories:
36
-
37
- ```bash
38
- gitstar pollmix 5
39
- ```
@@ -1 +0,0 @@
1
- from .core import main
@@ -1,77 +0,0 @@
1
- import json
2
- import math
3
- import sys
4
- from urllib.error import HTTPError
5
- from urllib.request import Request, urlopen
6
-
7
-
8
- def fetch(params):
9
- url = "https://api.github.com/"
10
- httprequest = Request(url + params, headers={"Accept": "application/json"})
11
-
12
- try:
13
- response = urlopen(httprequest)
14
- return json.loads(response.read().decode())
15
- except HTTPError as e:
16
- print(e.reason)
17
- exit()
18
-
19
-
20
- USAGE = """
21
- Find out the total star earned by a github user.
22
-
23
- Basic command:
24
- gitstar theusername
25
-
26
- Add printing limit:
27
- gitstar theusername 5
28
- """
29
-
30
-
31
- def main():
32
- # Debug print
33
- # print(f"DEBUG: sys.argv = {sys.argv}", file=sys.stderr)
34
- if len(sys.argv) == 1:
35
- print(USAGE)
36
- exit()
37
-
38
- limit = None
39
-
40
- if len(sys.argv) == 3:
41
- try:
42
- limit = int(sys.argv[2])
43
- except ValueError:
44
- print("Limit must be an integer")
45
- exit()
46
-
47
- username = sys.argv[1]
48
-
49
- user = fetch(f"users/{username}")
50
-
51
- if not user["public_repos"]:
52
- print(f"{username} doesn't have any public repository.")
53
- exit()
54
-
55
- pages = math.ceil(user["public_repos"] / 100)
56
- star = 0
57
- highest_name = 0
58
- repo_star_map = {}
59
-
60
- for i in range(pages):
61
- repos = fetch(f"users/{username}/repos?per_page=100&page={i+1}")
62
-
63
- for repo in repos:
64
- repo_star_map[repo["html_url"]] = repo["stargazers_count"]
65
- star += repo["stargazers_count"]
66
- highest_name = max(highest_name, len(repo["html_url"]))
67
-
68
- print(f"Total ⭐️ {star}")
69
-
70
- for repo, star in sorted(repo_star_map.items(), key=lambda x: x[1], reverse=True)[
71
- :limit
72
- ]:
73
- print(f"{repo:-<{highest_name}} ⭐️ {star}")
74
-
75
-
76
- if __name__ == "__main__":
77
- main()
@@ -1,60 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: gitstar
3
- Version: 1.0.0
4
- Summary: Find out the total star earned by a github user
5
- Home-page: https://github.com/imranpollob/gitstar
6
- Author: Imran Pollob
7
- Author-email: imranpollob@gmail.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.6
12
- Description-Content-Type: text/markdown
13
- Dynamic: author
14
- Dynamic: author-email
15
- Dynamic: classifier
16
- Dynamic: description
17
- Dynamic: description-content-type
18
- Dynamic: home-page
19
- Dynamic: requires-python
20
- Dynamic: summary
21
-
22
- # Gitstar
23
-
24
- A command-line tool to calculate the total number of stars earned by a GitHub user.
25
-
26
- ## Installation
27
-
28
- Install via pip:
29
-
30
- ```bash
31
- pip install gitstar
32
- ```
33
-
34
- ## Usage
35
-
36
- Basic usage:
37
-
38
- ```bash
39
- gitstar <username>
40
- ```
41
-
42
- Limit the output to the top N repositories:
43
-
44
- ```bash
45
- gitstar <username> <limit>
46
- ```
47
-
48
- ### Examples
49
-
50
- Get total stars for user `pollmix`:
51
-
52
- ```bash
53
- gitstar pollmix
54
- ```
55
-
56
- Get total stars and list top 5 repositories:
57
-
58
- ```bash
59
- gitstar pollmix 5
60
- ```
@@ -1,3 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=42", "wheel"]
3
- build-backend = "setuptools.build_meta"
gitstar-1.0.0/setup.py DELETED
@@ -1,27 +0,0 @@
1
- from setuptools import setup, find_packages
2
-
3
- with open("README.md", "r", encoding="utf-8") as fh:
4
- long_description = fh.read()
5
-
6
- setup(
7
- name="gitstar",
8
- version="1.0.0",
9
- author="Imran Pollob",
10
- author_email="imranpollob@gmail.com",
11
- description="Find out the total star earned by a github user",
12
- long_description=long_description,
13
- long_description_content_type="text/markdown",
14
- url="https://github.com/imranpollob/gitstar",
15
- packages=find_packages(),
16
- classifiers=[
17
- "Programming Language :: Python :: 3",
18
- "License :: OSI Approved :: MIT License",
19
- "Operating System :: OS Independent",
20
- ],
21
- python_requires=">=3.6",
22
- entry_points={
23
- "console_scripts": [
24
- "gitstar=gitstar.core:main",
25
- ],
26
- },
27
- )