helper-cli 0.1.11__py3-none-any.whl → 0.1.21__py3-none-any.whl

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,202 @@
1
+ """File management commands."""
2
+ import os
3
+ import time
4
+ import click
5
+ from pathlib import Path
6
+ from typing import List, Optional, Tuple, Callable
7
+ from datetime import datetime
8
+
9
+
10
+ def get_sorted_files(
11
+ directory: str,
12
+ extension: Optional[str] = None,
13
+ sort_key: Callable[[os.DirEntry], float] = None,
14
+ reverse: bool = False
15
+ ) -> List[Tuple[os.DirEntry, float]]:
16
+ """Get files sorted by specified key.
17
+
18
+ Args:
19
+ directory: Directory to search in
20
+ extension: Optional file extension to filter by (without dot)
21
+ sort_key: Function to extract sort key from DirEntry
22
+ reverse: If True, sort in descending order
23
+
24
+ Returns:
25
+ List of (file_entry, sort_key) tuples
26
+ """
27
+ if not os.path.isdir(directory):
28
+ raise click.BadParameter(f"Directory not found: {directory}")
29
+
30
+ files = []
31
+ with os.scandir(directory) as it:
32
+ for entry in it:
33
+ if not entry.is_file():
34
+ continue
35
+
36
+ if extension and not entry.name.lower().endswith(f".{extension.lower()}"):
37
+ continue
38
+
39
+ if sort_key:
40
+ try:
41
+ key = sort_key(entry)
42
+ files.append((entry, key))
43
+ except (OSError, ValueError) as e:
44
+ continue
45
+ else:
46
+ files.append((entry, 0))
47
+
48
+ # Sort by the sort key
49
+ files.sort(key=lambda x: x[1], reverse=reverse)
50
+ return files
51
+
52
+
53
+ def format_file_info(entry: os.DirEntry, size: bool = True, modified: bool = True) -> str:
54
+ """Format file information for display."""
55
+ info = []
56
+ if size:
57
+ try:
58
+ size_bytes = entry.stat().st_size
59
+ size_str = human_readable_size(size_bytes)
60
+ info.append(f"{size_str:>10}")
61
+ except OSError:
62
+ info.append(" " * 10)
63
+
64
+ if modified:
65
+ try:
66
+ mtime = entry.stat().st_mtime
67
+ mtime_str = datetime.fromtimestamp(mtime).strftime('%Y-%m-%d %H:%M:%S')
68
+ info.append(mtime_str)
69
+ except OSError:
70
+ info.append(" " * 19)
71
+
72
+ info.append(entry.name)
73
+ return " ".join(info)
74
+
75
+
76
+ def human_readable_size(size_bytes: int) -> str:
77
+ """Convert size in bytes to human readable format."""
78
+ if size_bytes == 0:
79
+ return "0B"
80
+
81
+ units = ['B', 'KB', 'MB', 'GB', 'TB']
82
+ unit_idx = 0
83
+ size = float(size_bytes)
84
+
85
+ while size >= 1024 and unit_idx < len(units) - 1:
86
+ size /= 1024
87
+ unit_idx += 1
88
+
89
+ return f"{size:.1f}{units[unit_idx]}"
90
+
91
+
92
+ @click.group(name="file")
93
+ @click.option(
94
+ "--directory",
95
+ "-d",
96
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True),
97
+ default=".",
98
+ help="Directory to search in (default: current directory)",
99
+ )
100
+ @click.option(
101
+ "--extension",
102
+ "-e",
103
+ type=str,
104
+ help="Filter by file extension (without dot)",
105
+ )
106
+ @click.option(
107
+ "--size",
108
+ "-s",
109
+ is_flag=True,
110
+ help="Show file sizes",
111
+ )
112
+ @click.option(
113
+ "--modified",
114
+ "-m",
115
+ is_flag=True,
116
+ help="Show last modified time",
117
+ )
118
+ @click.pass_context
119
+ def file_cmd(ctx: click.Context, directory: str, extension: str, size: bool, modified: bool) -> None:
120
+ """File management and processing commands."""
121
+ ctx.ensure_object(dict)
122
+ ctx.obj["directory"] = directory
123
+ ctx.obj["extension"] = extension
124
+ ctx.obj["show_size"] = size
125
+ ctx.obj["show_modified"] = modified
126
+
127
+
128
+ @file_cmd.command(name="newest")
129
+ @click.option(
130
+ "--count",
131
+ "-n",
132
+ type=int,
133
+ default=1,
134
+ help="Number of newest files to show (default: 1)",
135
+ )
136
+ @click.pass_context
137
+ def newest_files(ctx: click.Context, count: int) -> None:
138
+ """Show the newest files in the directory."""
139
+ directory = ctx.obj["directory"]
140
+ extension = ctx.obj["extension"]
141
+ show_size = ctx.obj["show_size"]
142
+ show_modified = ctx.obj["show_modified"]
143
+
144
+ try:
145
+ files = get_sorted_files(
146
+ directory=directory,
147
+ extension=extension,
148
+ sort_key=lambda e: e.stat().st_mtime,
149
+ reverse=True
150
+ )
151
+
152
+ if not files:
153
+ click.echo("No files found.")
154
+ return
155
+
156
+ click.echo(f"Newest files in {directory}:")
157
+ for i, (entry, _) in enumerate(files[:count], 1):
158
+ click.echo(f"{i}. {format_file_info(entry, show_size, show_modified)}")
159
+
160
+ except Exception as e:
161
+ raise click.ClickException(str(e))
162
+
163
+
164
+ @file_cmd.command(name="oldest")
165
+ @click.option(
166
+ "--count",
167
+ "-n",
168
+ type=int,
169
+ default=1,
170
+ help="Number of oldest files to show (default: 1)",
171
+ )
172
+ @click.pass_context
173
+ def oldest_files(ctx: click.Context, count: int) -> None:
174
+ """Show the oldest files in the directory."""
175
+ directory = ctx.obj["directory"]
176
+ extension = ctx.obj["extension"]
177
+ show_size = ctx.obj["show_size"]
178
+ show_modified = ctx.obj["show_modified"]
179
+
180
+ try:
181
+ files = get_sorted_files(
182
+ directory=directory,
183
+ extension=extension,
184
+ sort_key=lambda e: e.stat().st_mtime,
185
+ reverse=False
186
+ )
187
+
188
+ if not files:
189
+ click.echo("No files found.")
190
+ return
191
+
192
+ click.echo(f"Oldest files in {directory}:")
193
+ for i, (entry, _) in enumerate(files[:count], 1):
194
+ click.echo(f"{i}. {format_file_info(entry, show_size, show_modified)}")
195
+
196
+ except Exception as e:
197
+ raise click.ClickException(str(e))
198
+
199
+
200
+ # Add this to register the command group
201
+ def file():
202
+ return file_cmd
@@ -1,8 +1,9 @@
1
1
  import platform
2
2
  import socket
3
3
  import shutil
4
- from ..utils import run_cmd
5
4
  import click
5
+ from helper import __version__
6
+ from helper.utils import run_cmd
6
7
 
7
8
  def get_internal_ip():
8
9
  """Get the internal IP address based on the operating system."""
@@ -23,5 +24,12 @@ def get_internal_ip():
23
24
 
24
25
  @click.command()
25
26
  def internal_ip():
26
- """Show local/internal IP"""
27
+ """Show local/internal IP address.
28
+
29
+ Version: {}
30
+
31
+ Displays the internal IP address of the current machine.
32
+ The command automatically detects the operating system and uses the
33
+ appropriate method to retrieve the IP address.
34
+ """.format(__version__)
27
35
  get_internal_ip()
helper/commands/nixos.py CHANGED
@@ -1,65 +1,89 @@
1
- import click
2
- import platform
1
+ """NixOS related commands for the helper CLI.
2
+
3
+ This module provides commands to manage NixOS packages and system operations.
4
+ """
5
+
3
6
  import subprocess
7
+ import sys
8
+
9
+ import click
10
+
4
11
 
5
12
  def check_nixos():
6
13
  """Check if running on NixOS."""
7
14
  try:
8
- with open('/etc/os-release') as f:
9
- return 'NixOS' in f.read()
15
+ with open("/etc/os-release") as f:
16
+ return "NixOS" in f.read()
10
17
  except FileNotFoundError:
11
18
  return False
12
- except Exception as e:
19
+ except (IOError, PermissionError) as e:
13
20
  click.echo(f"Warning: Could not check if running on NixOS: {e}", err=True)
14
21
  return False
15
22
 
23
+
16
24
  def get_nixos_version():
17
- """Get NixOS version information."""
25
+ """Get NixOS version information.
26
+
27
+ Returns:
28
+ str: NixOS version information or error message
29
+ """
18
30
  if not check_nixos():
19
31
  return "Not running NixOS"
20
-
32
+
21
33
  try:
22
- result = subprocess.run(['nixos-version'],
23
- capture_output=True,
24
- text=True)
25
- if result.returncode == 0:
26
- return result.stdout.strip()
27
- return "NixOS version could not be determined"
28
- except Exception as e:
29
- return f"Error getting NixOS version: {str(e)}"
34
+ result = subprocess.run(
35
+ ["nixos-version"], capture_output=True, text=True, check=True
36
+ )
37
+ return result.stdout.strip()
38
+ except subprocess.CalledProcessError as e:
39
+ return f"Error getting NixOS version: {e.stderr}"
40
+ except FileNotFoundError:
41
+ return "nixos-version command not found"
42
+
30
43
 
31
44
  @click.group()
32
45
  def nixos():
33
- """NixOS related commands."""
46
+ """NixOS related commands (v0.1.19)."""
34
47
  if not check_nixos():
35
- click.echo("Warning: Not running on NixOS. Some commands may not work as expected.", err=True)
48
+ click.echo(
49
+ "Warning: Not running on NixOS. Some commands may not work as expected.",
50
+ err=True,
51
+ )
52
+
36
53
 
37
54
  @nixos.command()
38
55
  def version():
39
56
  """Show NixOS version."""
40
57
  click.echo(get_nixos_version())
41
58
 
59
+
42
60
  @nixos.command()
43
- @click.argument('package', required=False)
61
+ @click.argument("package", required=False)
44
62
  def search(package):
45
63
  """Search for Nix packages."""
46
64
  if not package:
47
65
  click.echo("Please specify a package to search for")
48
66
  return
49
-
67
+
50
68
  try:
51
- result = subprocess.run(['nix-env', '-qa', package],
52
- capture_output=True,
53
- text=True)
69
+ result = subprocess.run(
70
+ ["nix-env", "-qa", package], capture_output=True, text=True, check=False
71
+ )
54
72
  if result.returncode == 0:
55
73
  click.echo(result.stdout)
56
74
  else:
57
75
  click.echo(f"Error searching for package: {result.stderr}", err=True)
58
- except Exception as e:
76
+ except subprocess.SubprocessError as e:
59
77
  click.echo(f"Error: {str(e)}", err=True)
60
78
 
79
+
61
80
  @nixos.command()
62
- @click.option('-f', '--force', is_flag=True, help='Force garbage collection and remove all old generations')
81
+ @click.option(
82
+ "-f",
83
+ "--force",
84
+ is_flag=True,
85
+ help="Force garbage collection and remove all old generations",
86
+ )
63
87
  def clean(force):
64
88
  """Clean Nix store and perform garbage collection."""
65
89
  if not check_nixos():
@@ -71,18 +95,20 @@ def clean(force):
71
95
  if force:
72
96
  click.echo("Forcing garbage collection and removing all old generations...")
73
97
  # Remove all old generations of all profiles
74
- subprocess.run(['nix-collect-garbage', '-d'], check=True)
98
+ subprocess.run(["nix-collect-garbage", "-d"], check=True)
75
99
  click.echo("✓ Removed all old generations and ran garbage collection")
76
100
  else:
77
101
  # Regular garbage collection (safe, only removes unreachable paths)
78
- subprocess.run(['nix-collect-garbage'], check=True)
102
+ subprocess.run(["nix-collect-garbage"], check=True)
79
103
  click.echo("✓ Garbage collection completed")
80
-
104
+
81
105
  # Show disk space usage after cleanup
82
106
  click.echo("\nDisk space usage after cleanup:")
83
- subprocess.run(['nix-store', '--query', '--disk-usage', '/nix/store'])
84
-
107
+ subprocess.run(
108
+ ["nix-store", "--query", "--disk-usage", "/nix/store"], check=False
109
+ )
110
+
85
111
  except subprocess.CalledProcessError as e:
86
112
  click.echo(f"Error during cleanup: {e}", err=True)
87
- except Exception as e:
88
- click.echo(f"Unexpected error: {str(e)}", err=True)
113
+ except subprocess.SubprocessError as e:
114
+ click.echo(f"Subprocess error: {str(e)}", err=True)
@@ -1,8 +1,17 @@
1
- from ..utils import run_cmd
2
1
  import click
2
+ from helper import __version__
3
+ from helper.utils import run_cmd
4
+
3
5
 
4
6
  @click.command()
5
7
  def public_ip():
6
- """Show public IP"""
8
+ """Show public IP address.
9
+
10
+ Version: {}
11
+
12
+ Retrieves and displays the public IP address of the current machine.
13
+ Uses https://ifconfig.me as the primary service and falls back to curl
14
+ if the primary method fails.
15
+ """.format(__version__)
7
16
  cmd = "curl -s ifconfig.me"
8
17
  run_cmd(cmd)
@@ -0,0 +1,38 @@
1
+ import click
2
+ import subprocess
3
+ from ..utils import run_cmd
4
+
5
+
6
+ def check_speedtest_installed():
7
+ """Check if speedtest-cli is installed."""
8
+ try:
9
+ subprocess.run(
10
+ ["which", "speedtest-cli"],
11
+ check=True,
12
+ stdout=subprocess.PIPE,
13
+ stderr=subprocess.PIPE,
14
+ )
15
+ return True
16
+ except (subprocess.CalledProcessError, FileNotFoundError):
17
+ return False
18
+
19
+
20
+ @click.command()
21
+ @click.option("--simple", "-s", is_flag=True, help="Only show basic speed information")
22
+ def speed(simple):
23
+ """Test internet speed using speedtest-cli"""
24
+ if not check_speedtest_installed():
25
+ click.echo("Error: speedtest-cli is not installed. Please install it first.")
26
+ click.echo("You can install it with: pip install speedtest-cli")
27
+ return
28
+
29
+ cmd = "speedtest-cli"
30
+ if simple:
31
+ cmd += " --simple"
32
+
33
+ run_cmd(cmd)
34
+
35
+
36
+ # Add aliases for the command
37
+ speed_test = speed
38
+ sp = speed