Skip to content

Commit 2c4e2d6

Browse files
committed
organizing mostly to test updates
1 parent 5f0d226 commit 2c4e2d6

1 file changed

Lines changed: 136 additions & 118 deletions

File tree

src/redfetch/meta.py

Lines changed: 136 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# Standard
1+
#region Imports
2+
# standard imports
23
import os
34
import platform
45
import subprocess
@@ -7,22 +8,25 @@
78
import tempfile
89
from pathlib import Path
910

10-
# Third-party
11+
# third-party imports
1112
import httpx
1213
from packaging import version
1314

14-
# Rich library
15+
# rich library
1516
from rich.console import Console
1617
from rich.panel import Panel
1718
from rich.text import Text
1819
from rich.prompt import Confirm
1920

20-
# Local
21+
# local imports
2122
from redfetch.__about__ import __version__
2223
from redfetch import config
2324
from diskcache import Cache
25+
#endregion Imports
2426

2527

28+
#region Module setup
29+
2630
def _get_pypi_url() -> str:
2731
"""Pick PyPI JSON URL, favouring `REDFETCH_PYPI_URL` if set."""
2832
env_url = os.getenv("REDFETCH_PYPI_URL")
@@ -41,6 +45,10 @@ def _get_pypi_url() -> str:
4145
def get_current_version():
4246
return __version__
4347

48+
#endregion Module setup
49+
50+
51+
#region Version caching
4452

4553
_UPDATE_CACHE_TTL_SECONDS = 10 * 60 # 10 minutes
4654

@@ -72,6 +80,20 @@ def clear_pypi_cache() -> None:
7280
_meta_cache = None
7381

7482

83+
def fetch_latest_version_from_pypi():
84+
response = httpx.get(PYPI_URL, timeout=10.0)
85+
response.raise_for_status()
86+
data = response.json()
87+
# On TestPyPI, prefer the highest available release (including pre-releases)
88+
if "test.pypi.org" in PYPI_URL:
89+
releases = list(data.get("releases", {}).keys())
90+
if releases:
91+
releases.sort(key=version.parse)
92+
return releases[-1]
93+
# Default: whatever PyPI reports as the latest stable version
94+
return data["info"]["version"]
95+
96+
7597
def fetch_latest_version_cached():
7698
"""Fetch latest PyPI version with a 2-hour disk-backed cache."""
7799
global _meta_cache
@@ -85,20 +107,10 @@ def fetch_latest_version_cached():
85107
_meta_cache.set(cache_key, latest, expire=_UPDATE_CACHE_TTL_SECONDS)
86108
return latest
87109

110+
#endregion Version caching
88111

89-
def fetch_latest_version_from_pypi():
90-
response = httpx.get(PYPI_URL, timeout=10.0)
91-
response.raise_for_status()
92-
data = response.json()
93-
# On TestPyPI, prefer the highest available release (including pre-releases)
94-
if "test.pypi.org" in PYPI_URL:
95-
releases = list(data.get("releases", {}).keys())
96-
if releases:
97-
releases.sort(key=version.parse)
98-
return releases[-1]
99-
# Default: whatever PyPI reports as the latest stable version
100-
return data["info"]["version"]
101112

113+
#region Updating
102114

103115
def get_executable_path():
104116
executable_path = os.environ.get('PYAPP')
@@ -167,56 +179,6 @@ def get_update_command():
167179
return commands.get(method)
168180

169181

170-
def check_for_update(relaunch: bool = False):
171-
current_version = get_current_version()
172-
173-
try:
174-
latest_version = fetch_latest_version_cached()
175-
176-
if version.parse(latest_version) > version.parse(current_version):
177-
version_info = Panel(
178-
Text.assemble(
179-
("An update for redfetch is available! 🚡\n\n", "bold green"),
180-
("Local version: ", "dim"),
181-
(f"{current_version}\n", "cyan"),
182-
("Latest version: ", "dim"),
183-
(f"{latest_version}", "cyan bold")
184-
),
185-
title="Update Available",
186-
expand=False
187-
)
188-
console.print(version_info)
189-
190-
# Handle PYAPP separately
191-
if os.getenv('PYAPP'):
192-
if Confirm.ask("Would you like to update now?"):
193-
return self_update(relaunch=relaunch)
194-
else:
195-
console.print("[yellow]Update skipped. You can manually update later.[/yellow]")
196-
return False
197-
198-
# Get the appropriate update command
199-
update_command = get_update_command()
200-
if not update_command:
201-
console.print("[red]Could not determine update method.[/red]")
202-
return False
203-
204-
command_panel = Panel(
205-
Text(" ".join(update_command), style="bold cyan"),
206-
title="Update Command",
207-
expand=False
208-
)
209-
console.print(command_panel)
210-
211-
if Confirm.ask("Would you like to run this command to update?"):
212-
return pip_update_redfetch(update_command, latest_version)
213-
else:
214-
console.print("[yellow]Update skipped. You can manually update later.[/yellow]")
215-
except Exception as e:
216-
console.print(f"[bold red]Error checking for updates:[/bold red] {e}")
217-
return False
218-
219-
220182
def pip_update_redfetch(update_command, latest_version):
221183
try:
222184
console.print(f"\n[bold]Updating redfetch to version {latest_version}...[/bold]\n")
@@ -289,6 +251,60 @@ def self_update(relaunch: bool = False):
289251
sys.exit(1)
290252

291253

254+
def check_for_update(relaunch: bool = False):
255+
current_version = get_current_version()
256+
257+
try:
258+
latest_version = fetch_latest_version_cached()
259+
260+
if version.parse(latest_version) > version.parse(current_version):
261+
version_info = Panel(
262+
Text.assemble(
263+
("An update for redfetch is available! 🚡\n\n", "bold green"),
264+
("Local version: ", "dim"),
265+
(f"{current_version}\n", "cyan"),
266+
("Latest version: ", "dim"),
267+
(f"{latest_version}", "cyan bold")
268+
),
269+
title="Update Available",
270+
expand=False
271+
)
272+
console.print(version_info)
273+
274+
# Handle PYAPP separately
275+
if os.getenv('PYAPP'):
276+
if Confirm.ask("Would you like to update now?"):
277+
return self_update(relaunch=relaunch)
278+
else:
279+
console.print("[yellow]Update skipped. You can manually update later.[/yellow]")
280+
return False
281+
282+
# Get the appropriate update command
283+
update_command = get_update_command()
284+
if not update_command:
285+
console.print("[red]Could not determine update method.[/red]")
286+
return False
287+
288+
command_panel = Panel(
289+
Text(" ".join(update_command), style="bold cyan"),
290+
title="Update Command",
291+
expand=False
292+
)
293+
console.print(command_panel)
294+
295+
if Confirm.ask("Would you like to run this command to update?"):
296+
return pip_update_redfetch(update_command, latest_version)
297+
else:
298+
console.print("[yellow]Update skipped. You can manually update later.[/yellow]")
299+
except Exception as e:
300+
console.print(f"[bold red]Error checking for updates:[/bold red] {e}")
301+
return False
302+
303+
#endregion Updating
304+
305+
306+
#region Uninstalling
307+
292308
def self_remove():
293309
"""Remove with PYAPP."""
294310
try:
@@ -377,6 +393,59 @@ def _release_disk_caches() -> None:
377393
raise RuntimeError("; ".join(errors))
378394

379395

396+
def generate_removal_commands(paths):
397+
"""Generate OS-specific commands to remove the given directories."""
398+
system = platform.system()
399+
if system == 'Windows':
400+
# Generate PowerShell commands
401+
console.print("[bold]These directories may be removed manually after you make sure there's nothing you need from them, you can do so by running the following PowerShell commands:[/bold]\n")
402+
commands = []
403+
for path in sorted(paths):
404+
# Escape quotes and handle special characters
405+
escaped_path = path.replace("'", "''")
406+
command = f"Remove-Item -LiteralPath '{escaped_path}' -Recurse -Force"
407+
commands.append(command)
408+
console.print(f" {command}")
409+
else:
410+
# Assuming Unix-like system
411+
console.print("[bold]You can remove these directories by running the following commands in your terminal:[/bold]\n")
412+
commands = []
413+
for path in sorted(paths):
414+
# Escape single quotes
415+
escaped_path = path.replace("'", "'\\''")
416+
command = f"rm -rf '{escaped_path}'"
417+
commands.append(command)
418+
console.print(f" {command}")
419+
console.print("\n[bold yellow]These directories must be removed manually.[/bold yellow]")
420+
return commands
421+
422+
423+
def write_commands_to_file(commands, paths):
424+
"""Write the removal commands and additional information to a text file and open it on Windows."""
425+
# Only write and open the file on Windows
426+
if platform.system() == 'Windows':
427+
file_path = os.path.join(os.path.expanduser("~"), "redfetch_removal_commands.txt")
428+
with open(file_path, 'w') as file:
429+
file.write("Manual Cleanup Instructions:\n")
430+
file.write("The following directories may contain files downloaded by redfetch. You can remove them manually if you want:\n")
431+
for path in sorted(paths):
432+
file.write(f" - {path}\n")
433+
file.write("\nMake sure there's nothing you want in them. When ready to delete, you can use:\n\n")
434+
435+
for command in commands:
436+
file.write(command + '\n')
437+
438+
# Automatically open the file with the default text editor
439+
try:
440+
os.startfile(file_path)
441+
except Exception as e:
442+
console.print(f"[red]Failed to open the file: {e}[/red]")
443+
console.print(f"Please open the file manually: [cyan]{file_path}[/cyan]")
444+
else:
445+
# On non-Windows systems, the important information is already printed to the console
446+
console.print("[yellow]After that, you can remove the redfetch package.[/yellow]")
447+
448+
380449
def uninstall():
381450
"""Guide the user through the uninstallation process."""
382451
# Import the logout function from auth module
@@ -530,55 +599,4 @@ def should_print_path(path):
530599
# Optionally, exit the program
531600
sys.exit(0)
532601

533-
534-
def generate_removal_commands(paths):
535-
"""Generate OS-specific commands to remove the given directories."""
536-
system = platform.system()
537-
if system == 'Windows':
538-
# Generate PowerShell commands
539-
console.print("[bold]These directories may be removed manually after you make sure there's nothing you need from them, you can do so by running the following PowerShell commands:[/bold]\n")
540-
commands = []
541-
for path in sorted(paths):
542-
# Escape quotes and handle special characters
543-
escaped_path = path.replace("'", "''")
544-
command = f"Remove-Item -LiteralPath '{escaped_path}' -Recurse -Force"
545-
commands.append(command)
546-
console.print(f" {command}")
547-
else:
548-
# Assuming Unix-like system
549-
console.print("[bold]You can remove these directories by running the following commands in your terminal:[/bold]\n")
550-
commands = []
551-
for path in sorted(paths):
552-
# Escape single quotes
553-
escaped_path = path.replace("'", "'\\''")
554-
command = f"rm -rf '{escaped_path}'"
555-
commands.append(command)
556-
console.print(f" {command}")
557-
console.print("\n[bold yellow]These directories must be removed manually.[/bold yellow]")
558-
return commands
559-
560-
561-
def write_commands_to_file(commands, paths):
562-
"""Write the removal commands and additional information to a text file and open it on Windows."""
563-
# Only write and open the file on Windows
564-
if platform.system() == 'Windows':
565-
file_path = os.path.join(os.path.expanduser("~"), "redfetch_removal_commands.txt")
566-
with open(file_path, 'w') as file:
567-
file.write("Manual Cleanup Instructions:\n")
568-
file.write("The following directories may contain files downloaded by redfetch. You can remove them manually if you want:\n")
569-
for path in sorted(paths):
570-
file.write(f" - {path}\n")
571-
file.write("\nMake sure there's nothing you want in them. When ready to delete, you can use:\n\n")
572-
573-
for command in commands:
574-
file.write(command + '\n')
575-
576-
# Automatically open the file with the default text editor
577-
try:
578-
os.startfile(file_path)
579-
except Exception as e:
580-
console.print(f"[red]Failed to open the file: {e}[/red]")
581-
console.print(f"Please open the file manually: [cyan]{file_path}[/cyan]")
582-
else:
583-
# On non-Windows systems, the important information is already printed to the console
584-
console.print("[yellow]After that, you can remove the redfetch package.[/yellow]")
602+
#endregion Uninstalling

0 commit comments

Comments
 (0)