pypi-info 0.51__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.
@@ -0,0 +1,8 @@
1
+ include README.md
2
+ include pypi_info/__version__.py
3
+ exclude pypi_info/pipinfo1.py
4
+ exclude pypi_info/batmaker.py
5
+ exclude pypi_info/batmaker.ini
6
+ exclude pypi_info/mpath.py
7
+ exclude pypi_info/__main__1.py
8
+ exclude __version__.py
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.1
2
+ Name: pypi_info
3
+ Version: 0.51
4
+ Summary: Search and get description of package on pypi.org
5
+ Home-page: https://github.com/cumulus13/pipinfo
6
+ Author: Hadi Cahyadi
7
+ Author-email: cumulus13@gmail.com
8
+ License: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
17
+ Requires-Python: >=3.7
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: rich
20
+ Requires-Dist: rich_argparse
21
+
22
+ # pypi-info
23
+
24
+ 🐍 PyPI Package Information Tool - Get detailed info about Python packages
25
+
26
+ ## usage
27
+ ```bash:
28
+ Usage: pypi-info [-h] [-l] [-d] [-p PATH] [--version-download VERSION_DOWNLOAD] [--author] [--home] [--tags] [--urls] [-v]
29
+ [package]
30
+
31
+ 🐍 PyPI Package Information Tool - Get detailed info about Python packages
32
+
33
+ Positional Arguments:
34
+ package 📦 Package name to search for
35
+
36
+ Options:
37
+ -h, --help show this help message and exit
38
+ -l, --last 🔍 Show only the latest version information
39
+ -d, --download 📥 Download the package with progress bar
40
+ -p, --path PATH 📁 Directory path to save downloaded files (default: current directory)
41
+ --version-download VERSION_DOWNLOAD
42
+ 🔢 Specific version to download (default: latest)
43
+ --author 👤 Show author information
44
+ --home 🏠 Show home page URL
45
+ --tags 🏷️ Show package classifiers/tags
46
+ --urls 🔗 Show all project URLs
47
+ -v, --version Show version
48
+ ```
49
+
50
+ ## author
51
+ [Hadi Cahyadi](mailto:cumulus13@gmail.com)
52
+
53
+ ## requirements
54
+ - rich
55
+ - rich_argparse
56
+
57
+ [![Buy Me a Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/cumulus13)
58
+
59
+ [![Donate via Ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/cumulus13)
60
+
61
+ [Support me on Patreon](https://www.patreon.com/cumulus13)
@@ -0,0 +1,40 @@
1
+ # pypi-info
2
+
3
+ 🐍 PyPI Package Information Tool - Get detailed info about Python packages
4
+
5
+ ## usage
6
+ ```bash:
7
+ Usage: pypi-info [-h] [-l] [-d] [-p PATH] [--version-download VERSION_DOWNLOAD] [--author] [--home] [--tags] [--urls] [-v]
8
+ [package]
9
+
10
+ 🐍 PyPI Package Information Tool - Get detailed info about Python packages
11
+
12
+ Positional Arguments:
13
+ package 📦 Package name to search for
14
+
15
+ Options:
16
+ -h, --help show this help message and exit
17
+ -l, --last 🔍 Show only the latest version information
18
+ -d, --download 📥 Download the package with progress bar
19
+ -p, --path PATH 📁 Directory path to save downloaded files (default: current directory)
20
+ --version-download VERSION_DOWNLOAD
21
+ 🔢 Specific version to download (default: latest)
22
+ --author 👤 Show author information
23
+ --home 🏠 Show home page URL
24
+ --tags 🏷️ Show package classifiers/tags
25
+ --urls 🔗 Show all project URLs
26
+ -v, --version Show version
27
+ ```
28
+
29
+ ## author
30
+ [Hadi Cahyadi](mailto:cumulus13@gmail.com)
31
+
32
+ ## requirements
33
+ - rich
34
+ - rich_argparse
35
+
36
+ [![Buy Me a Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/cumulus13)
37
+
38
+ [![Donate via Ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/cumulus13)
39
+
40
+ [Support me on Patreon](https://www.patreon.com/cumulus13)
@@ -0,0 +1 @@
1
+ from .pipinfo import *
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env python
2
+
3
+ if __name__ == '__main__':
4
+ import sys
5
+ try:
6
+ from .pipinfo import main
7
+ except:
8
+ from pipinfo import main
9
+ sys.exit(main())
@@ -0,0 +1 @@
1
+ version = "0.51"
@@ -0,0 +1,606 @@
1
+ #!/usr/bin/env python3
2
+ # author: Hadi Cahyadi (cumulus13@gmail.com)
3
+ # create in 10 minutes
4
+
5
+ """
6
+ PyPI Package Information Tool
7
+ A beautiful command-line tool to fetch and display PyPI package information.
8
+ """
9
+
10
+ import argparse
11
+ import json
12
+ import os
13
+ import sys
14
+ import urllib.request
15
+ # import urllib.parse
16
+ from pathlib import Path
17
+ from typing import Dict, Any, List, Optional
18
+ from datetime import datetime
19
+ # import tempfile
20
+ # import shutil
21
+
22
+ try:
23
+ from rich.console import Console
24
+ from rich.table import Table
25
+ from rich.panel import Panel
26
+ from rich.columns import Columns
27
+ from rich.text import Text
28
+ from rich.markdown import Markdown
29
+ from rich.progress import Progress, BarColumn, TextColumn, TimeRemainingColumn
30
+ from rich.tree import Tree
31
+ from rich.align import Align
32
+ from rich_argparse import RichHelpFormatter, _lazy_rich as rr
33
+ except ImportError:
34
+ print("❌ Error: rich and rich-argparse packages are required!")
35
+ print("Install with: pip install rich rich-argparse")
36
+ sys.exit(1)
37
+
38
+ console = Console()
39
+
40
+ class CustomRichHelpFormatter(RichHelpFormatter):
41
+ """A custom RichHelpFormatter with modified styles."""
42
+
43
+ styles: dict[str, rr.StyleType] = {
44
+ "argparse.args": "bold #FFFF00", # Yellow
45
+ "argparse.groups": "#AA55FF", # Purple
46
+ "argparse.help": "bold #00FFFF", # Cyan
47
+ "argparse.metavar": "bold #FF00FF", # Magenta
48
+ "argparse.syntax": "underline", # Underlined
49
+ "argparse.text": "white", # White
50
+ "argparse.prog": "bold #00AAFF italic", # Blue italic
51
+ "argparse.default": "bold", # Bold
52
+ }
53
+
54
+ class PyPIClient:
55
+ """Client for interacting with PyPI API."""
56
+
57
+ BASE_URL = "https://pypi.org/pypi"
58
+
59
+ def __init__(self):
60
+ self.session_headers = {
61
+ 'User-Agent': 'PyPI-Info-Tool/1.0 (https://github.com/user/pypi-info-tool)'
62
+ }
63
+
64
+ def get_package_info(self, package_name: str) -> Optional[Dict[str, Any]]:
65
+ """Fetch package information from PyPI API."""
66
+ url = f"{self.BASE_URL}/{package_name}/json"
67
+
68
+ try:
69
+ with console.status(f"[bold blue]🔍 Searching for package '{package_name}'...", spinner="dots"):
70
+ req = urllib.request.Request(url, headers=self.session_headers)
71
+ with urllib.request.urlopen(req, timeout=10) as response:
72
+ if response.status == 200:
73
+ return json.loads(response.read().decode('utf-8'))
74
+ else:
75
+ return None
76
+ except urllib.error.HTTPError as e:
77
+ if e.code == 404:
78
+ console.print(f"[red]❌ Package '{package_name}' not found on PyPI[/red]")
79
+ else:
80
+ console.print(f"[red]❌ HTTP Error {e.code}: {e.reason}[/red]")
81
+ return None
82
+ except Exception as e:
83
+ console.print(f"[red]❌ Error fetching package info: {str(e)}[/red]")
84
+ return None
85
+
86
+ def download_package(self, package_name: str, version: str = None,
87
+ download_path: str = ".", progress_callback=None) -> bool:
88
+ """Download package from PyPI."""
89
+ package_info = self.get_package_info(package_name)
90
+ if not package_info:
91
+ return False
92
+
93
+ # Get the version to download
94
+ if version is None or version == "latest":
95
+ version = package_info['info']['version']
96
+
97
+ # Find the download URL
98
+ releases = package_info.get('releases', {})
99
+ if version not in releases:
100
+ console.print(f"[red]❌ Version {version} not found for {package_name}[/red]")
101
+ return False
102
+
103
+ files = releases[version]
104
+ if not files:
105
+ console.print(f"[red]❌ No files available for {package_name} {version}[/red]")
106
+ return False
107
+
108
+ # Prefer wheel files, then source distributions
109
+ download_file = None
110
+ for file_info in files:
111
+ if file_info['packagetype'] == 'bdist_wheel':
112
+ download_file = file_info
113
+ break
114
+
115
+ if not download_file:
116
+ for file_info in files:
117
+ if file_info['packagetype'] == 'sdist':
118
+ download_file = file_info
119
+ break
120
+
121
+ if not download_file:
122
+ download_file = files[0] # Fallback to first available
123
+
124
+ # Download the file
125
+ download_url = download_file['url']
126
+ filename = download_file['filename']
127
+ file_size = download_file.get('size', 0)
128
+
129
+ download_path = Path(download_path)
130
+ download_path.mkdir(parents=True, exist_ok=True)
131
+ filepath = download_path / filename
132
+
133
+ try:
134
+ with Progress(
135
+ TextColumn("[bold blue]📥 Downloading"),
136
+ TextColumn("[bold yellow]{task.fields[filename]}"),
137
+ BarColumn(bar_width=40),
138
+ TextColumn("[progress.percentage]{task.percentage:>3.1f}%"),
139
+ TextColumn("[blue]({task.completed:,}/{task.total:,} bytes)"),
140
+ TimeRemainingColumn(),
141
+ console=console,
142
+ transient=False
143
+ ) as progress:
144
+
145
+ task = progress.add_task(
146
+ "download",
147
+ filename=filename,
148
+ total=file_size if file_size > 0 else None
149
+ )
150
+
151
+ req = urllib.request.Request(download_url, headers=self.session_headers)
152
+ with urllib.request.urlopen(req) as response:
153
+ with open(filepath, 'wb') as f:
154
+ downloaded = 0
155
+ while True:
156
+ chunk = response.read(8192)
157
+ if not chunk:
158
+ break
159
+ f.write(chunk)
160
+ downloaded += len(chunk)
161
+ if file_size > 0:
162
+ progress.update(task, completed=downloaded)
163
+ elif progress_callback:
164
+ progress_callback(downloaded)
165
+
166
+ console.print(f"[green]✅ Downloaded: {filepath}[/green]")
167
+ return True
168
+
169
+ except Exception as e:
170
+ console.print(f"[red]❌ Download failed: {str(e)}[/red]")
171
+ return False
172
+
173
+ class PackageInfoDisplay:
174
+ """Display package information in a beautiful format."""
175
+
176
+ def __init__(self):
177
+ self.console = console
178
+
179
+ def format_size(self, size_bytes: int) -> str:
180
+ """Format file size in human readable format."""
181
+ if size_bytes == 0:
182
+ return "0 B"
183
+
184
+ size_names = ["B", "KB", "MB", "GB"]
185
+ i = 0
186
+ while size_bytes >= 1024 and i < len(size_names) - 1:
187
+ size_bytes /= 1024.0
188
+ i += 1
189
+
190
+ return f"{size_bytes:.1f} {size_names[i]}"
191
+
192
+ def format_date(self, date_str: str) -> str:
193
+ """Format ISO date string to readable format."""
194
+ try:
195
+ dt = datetime.fromisoformat(date_str.replace('Z', '+00:00'))
196
+ return dt.strftime("%B %d, %Y")
197
+ except:
198
+ return date_str
199
+
200
+ def create_header_panel(self, info: Dict[str, Any]) -> Panel:
201
+ """Create the main header panel."""
202
+ name = info.get('name', 'Unknown')
203
+ version = info.get('version', 'Unknown')
204
+ summary = info.get('summary', 'No description available')
205
+
206
+ # Create title with emoji
207
+ title_text = Text()
208
+ title_text.append("📦 ", style="bold blue")
209
+ title_text.append(name, style="bold white")
210
+ title_text.append(f" {version}", style="bold green")
211
+
212
+ # Summary
213
+ summary_text = Text(summary, style="italic cyan")
214
+
215
+ content = Align.center(
216
+ Text.assemble(
217
+ title_text, "\n\n",
218
+ summary_text
219
+ )
220
+ )
221
+
222
+ return Panel(
223
+ content,
224
+ title="[bold blue]📋 Package Information[/bold blue]",
225
+ border_style="blue",
226
+ padding=(1, 2)
227
+ )
228
+
229
+ def create_basic_info_table(self, info: Dict[str, Any]) -> Table:
230
+ """Create basic information table."""
231
+ table = Table(show_header=False, box=None, padding=(0, 1))
232
+ table.add_column("Property", style="bold yellow", width=20)
233
+ table.add_column("Value", style="white")
234
+
235
+ # Basic info
236
+ basic_fields = [
237
+ ("🏷️ Name", info.get('name', 'N/A')),
238
+ ("🔢 Version", info.get('version', 'N/A')),
239
+ ("👤 Author", info.get('author', 'N/A')),
240
+ ("📧 Author Email", info.get('author_email', 'N/A')),
241
+ ("🏠 Home Page", info.get('home_page', 'N/A')),
242
+ ("🛠️ Maintainer", info.get('maintainer', 'N/A')),
243
+ ("📨 Maintainer Email", info.get('maintainer_email', 'N/A')),
244
+ ("📄 License", info.get('license', 'N/A')),
245
+ ("🐍 Python Requires", info.get('requires_python', 'N/A')),
246
+ ]
247
+
248
+ for prop, value in basic_fields:
249
+ if value and value != 'N/A':
250
+ # Truncate long values
251
+ if len(str(value)) > 50:
252
+ value = str(value)[:47] + "..."
253
+ table.add_row(prop, str(value))
254
+
255
+ return table
256
+
257
+ def create_urls_table(self, info: Dict[str, Any]) -> Optional[Table]:
258
+ """Create project URLs table."""
259
+ project_urls = info.get('project_urls', {})
260
+ if not project_urls:
261
+ return None
262
+
263
+ table = Table(title="🔗 Project URLs", show_header=False, box=None, padding=(0, 1))
264
+ table.add_column("Type", style="bold cyan", width=15)
265
+ table.add_column("URL", style="blue underline")
266
+
267
+ for url_type, url in project_urls.items():
268
+ if url:
269
+ # Truncate very long URLs
270
+ display_url = url if len(url) <= 60 else url[:57] + "..."
271
+ table.add_row(f"🌐 {url_type}", display_url)
272
+
273
+ return table
274
+
275
+ def create_classifiers_tree(self, info: Dict[str, Any]) -> Optional[Tree]:
276
+ """Create classifiers tree."""
277
+ classifiers = info.get('classifiers', [])
278
+ if not classifiers:
279
+ return None
280
+
281
+ tree = Tree("🏷️ [bold yellow]Classifiers")
282
+
283
+ # Group classifiers by category
284
+ categories = {}
285
+ for classifier in classifiers:
286
+ parts = classifier.split(' :: ')
287
+ if len(parts) >= 2:
288
+ category = parts[0]
289
+ subcategory = ' :: '.join(parts[1:])
290
+ if category not in categories:
291
+ categories[category] = []
292
+ categories[category].append(subcategory)
293
+
294
+ for category, items in categories.items():
295
+ category_node = tree.add(f"[bold cyan]{category}")
296
+ for item in items[:5]: # Limit to 5 items per category
297
+ category_node.add(f"[white]{item}")
298
+ if len(items) > 5:
299
+ category_node.add(f"[dim]... and {len(items) - 5} more")
300
+
301
+ return tree
302
+
303
+ def create_releases_table(self, releases: Dict[str, List], latest_version: str) -> Table:
304
+ """Create releases table showing recent versions."""
305
+ table = Table(title="📦 Recent Releases", box=None)
306
+ table.add_column("Version", style="bold green", width=15)
307
+ table.add_column("Release Date", style="cyan", width=20)
308
+ table.add_column("Files", style="yellow", width=10)
309
+ table.add_column("Size", style="magenta", width=12)
310
+
311
+ # Sort versions by upload time (newest first)
312
+ version_data = []
313
+ for version, files in releases.items():
314
+ if files:
315
+ upload_time = files[0].get('upload_time_iso_8601', '')
316
+ total_size = sum(f.get('size', 0) for f in files)
317
+ version_data.append((version, upload_time, len(files), total_size))
318
+
319
+ # Sort by upload time (newest first) and take top 10
320
+ version_data.sort(key=lambda x: x[1], reverse=True)
321
+
322
+ for i, (version, upload_time, file_count, total_size) in enumerate(version_data[:10]):
323
+ version_display = version
324
+ if version == latest_version:
325
+ version_display = f"{version} [bold red](latest)[/bold red]"
326
+
327
+ date_display = self.format_date(upload_time) if upload_time else "Unknown"
328
+ size_display = self.format_size(total_size) if total_size > 0 else "Unknown"
329
+
330
+ table.add_row(
331
+ version_display,
332
+ date_display,
333
+ str(file_count),
334
+ size_display
335
+ )
336
+
337
+ return table
338
+
339
+ def display_package_info(self, package_data: Dict[str, Any], show_last_only: bool = False):
340
+ """Display complete package information."""
341
+ info = package_data.get('info', {})
342
+ releases = package_data.get('releases', {})
343
+
344
+ # Header
345
+ self.console.print()
346
+ self.console.print(self.create_header_panel(info))
347
+ self.console.print()
348
+
349
+ if show_last_only:
350
+ # Show only latest version info
351
+ latest_version = info.get('version', 'Unknown')
352
+ latest_files = releases.get(latest_version, [])
353
+
354
+ if latest_files:
355
+ table = Table(title=f"📦 Latest Version ({latest_version})", box=None)
356
+ table.add_column("File", style="bold green")
357
+ table.add_column("Type", style="cyan")
358
+ table.add_column("Size", style="yellow")
359
+ table.add_column("Upload Date", style="magenta")
360
+
361
+ for file_info in latest_files:
362
+ table.add_row(
363
+ file_info.get('filename', 'Unknown'),
364
+ file_info.get('packagetype', 'Unknown'),
365
+ self.format_size(file_info.get('size', 0)),
366
+ self.format_date(file_info.get('upload_time_iso_8601', ''))
367
+ )
368
+
369
+ self.console.print(table)
370
+ else:
371
+ self.console.print("[yellow]⚠️ No files found for latest version[/yellow]")
372
+ return
373
+
374
+ # Create layout columns
375
+ left_column = []
376
+ right_column = []
377
+
378
+ # Basic info (left column)
379
+ basic_table = self.create_basic_info_table(info)
380
+ left_column.append(Panel(basic_table, title="[bold green]ℹ️ Basic Information", border_style="green"))
381
+
382
+ # URLs (right column)
383
+ urls_table = self.create_urls_table(info)
384
+ if urls_table:
385
+ right_column.append(Panel(urls_table, title="[bold blue]🔗 Links", border_style="blue"))
386
+
387
+ # Display two columns
388
+ if left_column and right_column:
389
+ self.console.print(Columns([left_column[0], right_column[0]], equal=True, expand=True))
390
+ self.console.print()
391
+ elif left_column:
392
+ self.console.print(left_column[0])
393
+ self.console.print()
394
+
395
+ # Classifiers tree
396
+ classifiers_tree = self.create_classifiers_tree(info)
397
+ if classifiers_tree:
398
+ self.console.print(Panel(classifiers_tree, title="[bold yellow]🏷️ Categories", border_style="yellow"))
399
+ self.console.print()
400
+
401
+ # Description
402
+ description = info.get('description', '').strip()
403
+ if description and len(description) > 100:
404
+ # Try to render as markdown if it looks like markdown
405
+ if any(marker in description for marker in ['#', '*', '`', '```', '[', '](']):
406
+ try:
407
+ md = Markdown(description[:2000] + ("..." if len(description) > 2000 else ""))
408
+ self.console.print(Panel(md, title="[bold cyan]📖 Description", border_style="cyan"))
409
+ self.console.print()
410
+ except:
411
+ # Fallback to plain text
412
+ desc_text = description[:1000] + ("..." if len(description) > 1000 else "")
413
+ self.console.print(Panel(desc_text, title="[bold cyan]📖 Description", border_style="cyan"))
414
+ self.console.print()
415
+ else:
416
+ desc_text = description[:1000] + ("..." if len(description) > 1000 else "")
417
+ self.console.print(Panel(desc_text, title="[bold cyan]📖 Description", border_style="cyan"))
418
+ self.console.print()
419
+
420
+ # Recent releases
421
+ if releases:
422
+ releases_table = self.create_releases_table(releases, info.get('version', ''))
423
+ self.console.print(releases_table)
424
+ self.console.print()
425
+
426
+ # Statistics
427
+ total_files = sum(len(files) for files in releases.values())
428
+ total_size = sum(sum(f.get('size', 0) for f in files) for files in releases.values())
429
+
430
+ stats_table = Table(show_header=False, box=None, padding=(0, 2))
431
+ stats_table.add_column("Metric", style="bold yellow")
432
+ stats_table.add_column("Value", style="bold white")
433
+
434
+ stats_table.add_row("📊 Total Versions", str(len(releases)))
435
+ stats_table.add_row("📁 Total Files", str(total_files))
436
+ stats_table.add_row("💾 Total Size", self.format_size(total_size))
437
+
438
+ self.console.print(Panel(stats_table, title="[bold magenta]📈 Statistics", border_style="magenta"))
439
+
440
+ def get_version():
441
+ """
442
+ Get the version of the ddf module.
443
+ Version is taken from the __version__.py file if it exists.
444
+ The content of __version__.py should be:
445
+ version = "0.33"
446
+ """
447
+ try:
448
+ version_file = Path(__file__).parent / "__version__.py"
449
+ if version_file.is_file():
450
+ with open(version_file, "r") as f:
451
+ for line in f:
452
+ if line.strip().startswith("version"):
453
+ parts = line.split("=")
454
+ if len(parts) == 2:
455
+ return parts[1].strip().strip('"').strip("'")
456
+ except Exception as e:
457
+ if os.getenv('TRACEBACK') and os.getenv('TRACEBACK') in ['1', 'true', 'True']:
458
+ console.print_exception(show_locals=False)
459
+ else:
460
+ console.log(f"[white on red]ERROR:[/] [white on blue]{e}[/]")
461
+
462
+ return "UNKNOWN VERSION"
463
+
464
+
465
+ def main():
466
+ """Main function."""
467
+ parser = argparse.ArgumentParser(
468
+ description="🐍 PyPI Package Information Tool - Get detailed info about Python packages",
469
+ formatter_class=CustomRichHelpFormatter,
470
+ prog="pipinfo"
471
+ )
472
+
473
+ parser.add_argument(
474
+ 'package',
475
+ nargs='?',
476
+ help='📦 Package name to search for'
477
+ )
478
+
479
+ parser.add_argument(
480
+ '-l', '--last',
481
+ action='store_true',
482
+ help='🔍 Show only the latest version information'
483
+ )
484
+
485
+ parser.add_argument(
486
+ '-d', '--download',
487
+ action='store_true',
488
+ help='📥 Download the package with progress bar'
489
+ )
490
+
491
+ parser.add_argument(
492
+ '-p', '--path',
493
+ default='.',
494
+ help='📁 Directory path to save downloaded files (default: current directory)'
495
+ )
496
+
497
+ parser.add_argument(
498
+ '--version-download',
499
+ help='🔢 Specific version to download (default: latest)'
500
+ )
501
+
502
+ parser.add_argument(
503
+ '--author',
504
+ action='store_true',
505
+ help='👤 Show author information'
506
+ )
507
+
508
+ parser.add_argument(
509
+ '--home',
510
+ action='store_true',
511
+ help='🏠 Show home page URL'
512
+ )
513
+
514
+ parser.add_argument(
515
+ '--tags',
516
+ action='store_true',
517
+ help='🏷️ Show package classifiers/tags'
518
+ )
519
+
520
+ parser.add_argument(
521
+ '--urls',
522
+ action='store_true',
523
+ help='🔗 Show all project URLs'
524
+ )
525
+
526
+ parser.add_argument('-v', '--version', action='version', version=f"[bold #FFFF00]version:[/] [bold #00FFFF]{get_version()}[/]", help="Show version")
527
+
528
+ args = parser.parse_args()
529
+
530
+ # Show help if no package specified
531
+ if not args.package:
532
+ parser.print_help()
533
+ return
534
+
535
+ # Initialize client and display
536
+ client = PyPIClient()
537
+ display = PackageInfoDisplay()
538
+
539
+ # Get package information
540
+ console.print(f"\n[bold blue]🔍 Searching PyPI for '{args.package}'...[/bold blue]")
541
+ package_data = client.get_package_info(args.package)
542
+
543
+ if not package_data:
544
+ console.print(f"[red]❌ Could not find package '{args.package}' on PyPI[/red]")
545
+ return
546
+
547
+ info = package_data.get('info', {})
548
+
549
+ # Handle specific info requests
550
+ if args.author:
551
+ author = info.get('author', 'N/A')
552
+ author_email = info.get('author_email', 'N/A')
553
+ console.print(f"[bold yellow]👤 Author:[/bold yellow] {author}")
554
+ if author_email != 'N/A':
555
+ console.print(f"[bold yellow]📧 Email:[/bold yellow] {author_email}")
556
+ return
557
+
558
+ if args.home:
559
+ home_page = info.get('home_page') or info.get('project_urls', {}).get('Homepage', 'N/A')
560
+ console.print(f"[bold yellow]🏠 Home Page:[/bold yellow] {home_page}")
561
+ return
562
+
563
+ if args.tags:
564
+ classifiers = info.get('classifiers', [])
565
+ if classifiers:
566
+ console.print("[bold yellow]🏷️ Package Tags/Classifiers:[/bold yellow]")
567
+ for classifier in classifiers:
568
+ console.print(f" • {classifier}")
569
+ else:
570
+ console.print("[yellow]No classifiers found[/yellow]")
571
+ return
572
+
573
+ if args.urls:
574
+ project_urls = info.get('project_urls', {})
575
+ if project_urls:
576
+ console.print("[bold yellow]🔗 Project URLs:[/bold yellow]")
577
+ for url_type, url in project_urls.items():
578
+ console.print(f" 🌐 [cyan]{url_type}:[/cyan] {url}")
579
+ else:
580
+ console.print("[yellow]No project URLs found[/yellow]")
581
+ return
582
+
583
+ # Download package if requested
584
+ if args.download:
585
+ version = args.version_download or "latest"
586
+ console.print(f"\n[bold green]📥 Downloading {args.package} (version: {version})...[/bold green]")
587
+ success = client.download_package(args.package, version, args.path)
588
+ if not success:
589
+ return
590
+ console.print()
591
+
592
+ # Display package information
593
+ display.display_package_info(package_data, args.last)
594
+
595
+ # Final message
596
+ console.print(f"[dim]💡 Use --download to download this package, or --help for more options[/dim]")
597
+
598
+ if __name__ == "__main__":
599
+ try:
600
+ main()
601
+ except KeyboardInterrupt:
602
+ console.print("\n[yellow]⚠️ Operation cancelled by user[/yellow]")
603
+ sys.exit(1)
604
+ except Exception as e:
605
+ console.print(f"\n[red]❌ Unexpected error: {str(e)}[/red]")
606
+ sys.exit(1)
@@ -0,0 +1,61 @@
1
+ Metadata-Version: 2.1
2
+ Name: pypi_info
3
+ Version: 0.51
4
+ Summary: Search and get description of package on pypi.org
5
+ Home-page: https://github.com/cumulus13/pipinfo
6
+ Author: Hadi Cahyadi
7
+ Author-email: cumulus13@gmail.com
8
+ License: MIT
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
17
+ Requires-Python: >=3.7
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: rich
20
+ Requires-Dist: rich_argparse
21
+
22
+ # pypi-info
23
+
24
+ 🐍 PyPI Package Information Tool - Get detailed info about Python packages
25
+
26
+ ## usage
27
+ ```bash:
28
+ Usage: pypi-info [-h] [-l] [-d] [-p PATH] [--version-download VERSION_DOWNLOAD] [--author] [--home] [--tags] [--urls] [-v]
29
+ [package]
30
+
31
+ 🐍 PyPI Package Information Tool - Get detailed info about Python packages
32
+
33
+ Positional Arguments:
34
+ package 📦 Package name to search for
35
+
36
+ Options:
37
+ -h, --help show this help message and exit
38
+ -l, --last 🔍 Show only the latest version information
39
+ -d, --download 📥 Download the package with progress bar
40
+ -p, --path PATH 📁 Directory path to save downloaded files (default: current directory)
41
+ --version-download VERSION_DOWNLOAD
42
+ 🔢 Specific version to download (default: latest)
43
+ --author 👤 Show author information
44
+ --home 🏠 Show home page URL
45
+ --tags 🏷️ Show package classifiers/tags
46
+ --urls 🔗 Show all project URLs
47
+ -v, --version Show version
48
+ ```
49
+
50
+ ## author
51
+ [Hadi Cahyadi](mailto:cumulus13@gmail.com)
52
+
53
+ ## requirements
54
+ - rich
55
+ - rich_argparse
56
+
57
+ [![Buy Me a Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/cumulus13)
58
+
59
+ [![Donate via Ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/cumulus13)
60
+
61
+ [Support me on Patreon](https://www.patreon.com/cumulus13)
@@ -0,0 +1,13 @@
1
+ MANIFEST.in
2
+ README.md
3
+ setup.py
4
+ pypi_info/__init__.py
5
+ pypi_info/__main__.py
6
+ pypi_info/__version__.py
7
+ pypi_info/pipinfo.py
8
+ pypi_info.egg-info/PKG-INFO
9
+ pypi_info.egg-info/SOURCES.txt
10
+ pypi_info.egg-info/dependency_links.txt
11
+ pypi_info.egg-info/entry_points.txt
12
+ pypi_info.egg-info/requires.txt
13
+ pypi_info.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ pipinfo = pypi_info.pipinfo:main
3
+ pypi-info = pypi_info.pipinfo:main
4
+ pypi_info = pypi_info.pipinfo:main
@@ -0,0 +1,2 @@
1
+ rich
2
+ rich_argparse
@@ -0,0 +1 @@
1
+ pypi_info
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,63 @@
1
+ from setuptools import setup, find_packages
2
+ from pathlib import Path
3
+ import shutil
4
+
5
+ NAME = 'pypi_info'
6
+ shutil.copy('__version__.py', str(Path(NAME) / '__version__.py'))
7
+
8
+ def get_version():
9
+ try:
10
+ with open(f"{NAME.replace('-', '_')}/__version__.py", "r") as f:
11
+ for line in f:
12
+ if line.strip().startswith("version"):
13
+ parts = line.split("=")
14
+ if len(parts) == 2:
15
+ return parts[1].strip().strip('"').strip("'")
16
+ except Exception as e:
17
+ print(f"Error getting version: {e}")
18
+ return "0.1"
19
+
20
+ def requirements():
21
+ try:
22
+ with open('requirements.txt', 'r') as f:
23
+ return [line.strip() for line in f if line.strip()]
24
+ except Exception as e:
25
+ print(f"Error reading requirements: {e}")
26
+ return []
27
+
28
+ setup(
29
+ name=NAME,
30
+ version=get_version(),
31
+ # packages=find_packages(exclude=["*.pipinfo1.py"]),
32
+ packages=[NAME.replace("-","_")],
33
+ include_package_data=True,
34
+ # package_data={
35
+ # 'pipinfo': ['batmaker.ini'],
36
+ # },
37
+ install_requires=requirements(),
38
+ entry_points={
39
+ 'console_scripts': [
40
+ 'pipinfo = pypi_info.pipinfo:main',
41
+ 'pypi-info = pypi_info.pipinfo:main',
42
+ 'pypi_info = pypi_info.pipinfo:main',
43
+ ],
44
+ },
45
+ author="Hadi Cahyadi",
46
+ author_email="cumulus13@gmail.com",
47
+ description="Search and get description of package on pypi.org",
48
+ long_description=(Path(__file__).parent / "README.md").read_text(encoding="utf-8") if Path("README.md").exists() else "",
49
+ long_description_content_type="text/markdown",
50
+ url="https://github.com/cumulus13/pipinfo",
51
+ classifiers=[
52
+ "Development Status :: 4 - Beta",
53
+ "Environment :: Console",
54
+ "Intended Audience :: Developers",
55
+ "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
56
+ "Programming Language :: Python :: 3",
57
+ "Operating System :: OS Independent",
58
+ "Topic :: Software Development :: Libraries",
59
+ "Topic :: Internet :: WWW/HTTP :: Indexing/Search",
60
+ ],
61
+ python_requires='>=3.7',
62
+ license="MIT",
63
+ )