roysh 0.1__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.
roysh/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .shell import main
2
+
3
+ __version__ = "0.1.0"
roysh/shell.py ADDED
@@ -0,0 +1,263 @@
1
+ import sys
2
+ import os
3
+ import subprocess
4
+ import shlex
5
+
6
+ # Handle readline import for different platforms
7
+ try:
8
+ import readline
9
+ except ImportError:
10
+ import pyreadline3 as readline
11
+
12
+ def extract_after_third_slash(command):
13
+ """Extracts the portion of the command after the third slash (/)"""
14
+ parts = command.split("/")
15
+ if len(parts) > 3:
16
+ return "/".join(parts[3:])
17
+ return command
18
+
19
+ def get_executables_from_path():
20
+ """Get list of executable files from PATH directories"""
21
+ executables = []
22
+ extensions = ['.exe', '.bat', '.cmd', ''] if os.name == 'nt' else ['']
23
+ path_sep = ';' if os.name == 'nt' else ':'
24
+
25
+ for directory in os.environ.get("PATH", "").split(path_sep):
26
+ try:
27
+ for file in os.listdir(directory):
28
+ file_path = os.path.join(directory, file)
29
+ # Check if the file has an executable extension on Windows
30
+ if os.name == 'nt':
31
+ file_name, file_ext = os.path.splitext(file.lower())
32
+ if file_ext not in extensions:
33
+ continue
34
+ if os.path.isfile(file_path) and os.access(file_path, os.X_OK):
35
+ # Remove extension for consistency
36
+ base_name = os.path.splitext(file)[0]
37
+ executables.append(base_name)
38
+ except OSError:
39
+ continue
40
+ return executables
41
+
42
+ def get_common_prefix(matches):
43
+ """Find the longest common prefix of all matches"""
44
+ if not matches:
45
+ return ""
46
+ if len(matches) == 1:
47
+ return matches[0]
48
+
49
+ reference = matches[0]
50
+
51
+ for i in range(len(reference)):
52
+ for other in matches[1:]:
53
+ if i >= len(other) or other[i] != reference[i]:
54
+ return reference[:i]
55
+
56
+ return reference
57
+
58
+ def get_matches(text):
59
+ """Get all matching commands for the given text"""
60
+ builtins = ["echo", "exit", "type", "pwd", "cd", "list"]
61
+ all_commands = builtins + get_executables_from_path()
62
+ return sorted(set(cmd for cmd in all_commands if cmd.startswith(text)))
63
+
64
+ def complete(text, state):
65
+ """Autocomplete callback for readline"""
66
+ matches = get_matches(text)
67
+
68
+ if not matches:
69
+ return None
70
+
71
+ if state == 0:
72
+ if len(matches) == 1:
73
+ return matches[0] + " "
74
+ else:
75
+ common = get_common_prefix(matches)
76
+ if common != text:
77
+ return common
78
+ sys.stdout.write('\a')
79
+ sys.stdout.flush()
80
+ print()
81
+ print(" ".join(matches))
82
+ sys.stdout.write("$ " + text)
83
+ sys.stdout.flush()
84
+ return text
85
+
86
+ return None
87
+
88
+ def get_installed_packages():
89
+ """Get list of installed Python packages"""
90
+ try:
91
+ import pkg_resources
92
+ return sorted([f"{dist.key} {dist.version}" for dist in pkg_resources.working_set])
93
+ except Exception:
94
+ return ["Error: Unable to fetch installed packages"]
95
+
96
+ def get_roysh_commands():
97
+ """Get list of roysh commands with descriptions"""
98
+ commands = {
99
+ "echo": "Print text to stdout",
100
+ "exit": "Exit the shell (optional: exit <code>)",
101
+ "type": "Show command type/location",
102
+ "pwd": "Print working directory",
103
+ "cd": "Change directory (defaults to HOME)",
104
+ "list": "List available commands or installed packages",
105
+ }
106
+ return commands
107
+
108
+ def main():
109
+ # Display welcome message
110
+ print("\nRoysh - Shell by Python")
111
+ print("Developed by Nishan Roy")
112
+ print("Type 'exit' to quit\n")
113
+
114
+ # Set up readline with our completer
115
+ readline.parse_and_bind('tab: complete')
116
+ readline.set_completer(complete)
117
+
118
+ # Define the list of built-in commands
119
+ builtins = {"echo", "exit", "type", "pwd", "cd", "list"}
120
+
121
+ while True:
122
+ # Display the shell prompt
123
+ sys.stdout.write("$ ")
124
+ sys.stdout.flush()
125
+ try:
126
+ # Read user input (readline will handle tab completion)
127
+ command = input().strip()
128
+
129
+ # Check for output redirection
130
+ output_file = None
131
+ error_file = None
132
+ append_mode = False
133
+ append_error_mode = False
134
+
135
+ if " 2>> " in command:
136
+ parts = command.split(" 2>> ", 1)
137
+ command = parts[0].strip()
138
+ error_file = parts[1].strip()
139
+ append_error_mode = True
140
+ elif " 2> " in command:
141
+ parts = command.split(" 2> ", 1)
142
+ command = parts[0].strip()
143
+ error_file = parts[1].strip()
144
+ elif " >> " in command or " 1>> " in command:
145
+ parts = command.split(" >> ", 1) if " >> " in command else command.split(" 1>> ", 1)
146
+ command = parts[0].strip()
147
+ output_file = parts[1].strip()
148
+ append_mode = True
149
+ elif " > " in command or " 1> " in command:
150
+ parts = command.split(" > ", 1) if " > " in command else command.split(" 1> ", 1)
151
+ command = parts[0].strip()
152
+ output_file = parts[1].strip()
153
+
154
+ # Parse input to handle quotes
155
+ args = shlex.split(command, posix=True)
156
+ if not args:
157
+ continue
158
+
159
+ # Save original stdout/stderr if we need to redirect
160
+ original_stdout = None
161
+ original_stderr = None
162
+
163
+ try:
164
+ if output_file:
165
+ original_stdout = sys.stdout
166
+ sys.stdout = open(output_file, 'a' if append_mode else 'w')
167
+ if error_file:
168
+ original_stderr = sys.stderr
169
+ sys.stderr = open(error_file, 'a' if append_error_mode else 'w')
170
+
171
+ cmd = args[0]
172
+ # Handle built-in commands
173
+ if cmd == "exit":
174
+ if len(args) > 1 and args[1].isdigit():
175
+ exit_code = int(args[1])
176
+ else:
177
+ exit_code = 0
178
+ sys.exit(exit_code)
179
+ elif cmd == "list":
180
+ if len(args) > 1 and args[1] == "packages":
181
+ packages = get_installed_packages()
182
+ for package in packages:
183
+ print(package)
184
+ else:
185
+ print("\nAvailable Roysh Commands:")
186
+ print("----------------------")
187
+ commands = get_roysh_commands()
188
+ for cmd_name, description in commands.items():
189
+ print(f"{cmd_name:<10} - {description}")
190
+ print("\nUsage:")
191
+ print("list - Show this help")
192
+ print("list packages - Show installed Python packages\n")
193
+ elif cmd == "type":
194
+ if len(args) < 2:
195
+ print("type: missing operand")
196
+ continue
197
+ cmd_to_check = args[1]
198
+ if cmd_to_check in builtins:
199
+ print(f"{cmd_to_check} is a shell builtin")
200
+ else:
201
+ found = False
202
+ for directory in os.environ["PATH"].split(":"):
203
+ command_path = os.path.join(directory, cmd_to_check)
204
+ if os.path.isfile(command_path) and os.access(command_path, os.X_OK):
205
+ print(f"{cmd_to_check} is {command_path}")
206
+ found = True
207
+ break
208
+ if not found:
209
+ print(f"{cmd_to_check}: not found")
210
+ elif cmd == "pwd":
211
+ print(os.getcwd())
212
+ elif cmd == "cd":
213
+ if len(args) < 2:
214
+ target_dir = os.environ.get("HOME", "/")
215
+ else:
216
+ target_dir = args[1]
217
+ if target_dir == "~":
218
+ target_dir = os.environ.get("HOME", "/")
219
+ try:
220
+ os.chdir(target_dir)
221
+ except FileNotFoundError:
222
+ print(f"cd: {target_dir}: No such file or directory")
223
+ except PermissionError:
224
+ print(f"cd: {target_dir}: Permission denied")
225
+ elif cmd == "echo":
226
+ print(" ".join(args[1:]))
227
+ else:
228
+ found = False
229
+ extensions = ['.exe', '.bat', '.cmd', ''] if os.name == 'nt' else ['']
230
+ for directory in os.environ["PATH"].split(os.pathsep):
231
+ for ext in extensions:
232
+ program_path = os.path.join(directory, cmd + ext)
233
+ if os.path.isfile(program_path) and os.access(program_path, os.X_OK):
234
+ found = True
235
+ try:
236
+ result = subprocess.run(
237
+ [program_path] + args[1:],
238
+ capture_output=True,
239
+ text=True,
240
+ )
241
+ if result.stdout:
242
+ print(result.stdout.strip())
243
+ if result.stderr:
244
+ print(result.stderr.strip(), file=sys.stderr)
245
+ except Exception as e:
246
+ print(f"Error running {cmd}: {e}", file=sys.stderr)
247
+ break
248
+ if found:
249
+ break
250
+ if not found:
251
+ print(f"{cmd}: not found")
252
+ finally:
253
+ if output_file and original_stdout:
254
+ sys.stdout.close()
255
+ sys.stdout = original_stdout
256
+ if error_file and original_stderr:
257
+ sys.stderr.close()
258
+ sys.stderr = original_stderr
259
+ except EOFError:
260
+ sys.exit(0)
261
+
262
+ if __name__ == "__main__":
263
+ main()
@@ -0,0 +1,103 @@
1
+ Metadata-Version: 2.1
2
+ Name: roysh
3
+ Version: 0.1
4
+ Summary: A Python-based shell with tab completion
5
+ Home-page: https://github.com/nishanroy561/RoySH
6
+ Author: Nishan Roy
7
+ Author-email: nishanroy561@gmail.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: System :: Shells
14
+ Requires-Python: >=3.6
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: pyreadline3
17
+
18
+ # Roysh - A Python-based Shell
19
+
20
+ Roysh is a simple yet powerful Python-based shell that provides familiar shell functionality with tab completion support.
21
+
22
+ ## Features
23
+
24
+ - Interactive command-line interface
25
+ - Tab completion for commands and file paths
26
+ - Built-in shell commands
27
+ - Command output redirection
28
+ - Path-based command execution
29
+ - Cross-platform support
30
+
31
+ ## Installation
32
+
33
+ Install using pip:
34
+
35
+ ```bash
36
+ pip install roysh
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ Start the shell by running:
42
+
43
+ ```bash
44
+ roysh
45
+ ```
46
+
47
+ ### Built-in Commands
48
+
49
+ | Command | Description |
50
+ |---------|-------------|
51
+ | `echo [text]` | Print text to stdout |
52
+ | `exit [code]` | Exit the shell (optional status code) |
53
+ | `type <command>` | Show command type/location |
54
+ | `pwd` | Print working directory |
55
+ | `cd [path]` | Change directory (defaults to HOME) |
56
+
57
+ ### Output Redirection
58
+
59
+ Roysh supports standard output redirection operators:
60
+
61
+ | Operator | Description |
62
+ |----------|-------------|
63
+ | `>` | Redirect stdout to file (overwrite) |
64
+ | `>>` | Append stdout to file |
65
+ | `2>` | Redirect stderr to file (overwrite) |
66
+ | `2>>` | Append stderr to file |
67
+
68
+ ### Examples
69
+
70
+ ```bash
71
+ # Basic command usage
72
+ $ echo Hello World
73
+ Hello World
74
+
75
+ # Working with directories
76
+ $ pwd
77
+ /home/user
78
+ $ cd /tmp
79
+ $ pwd
80
+ /tmp
81
+
82
+ # Output redirection
83
+ $ echo "log entry" >> log.txt
84
+ $ echo "error message" 2> errors.txt
85
+
86
+ # Command information
87
+ $ type echo
88
+ echo is a shell builtin
89
+ $ type python
90
+ python is /usr/bin/python
91
+ ```
92
+
93
+ ## Contributing
94
+
95
+ Contributions are welcome! Please feel free to submit a Pull Request.
96
+
97
+ ## License
98
+
99
+ This project is licensed under the MIT License - see the LICENSE file for details.
100
+
101
+ ---
102
+ Developed by Nishan Roy
103
+ ```
@@ -0,0 +1,7 @@
1
+ roysh/__init__.py,sha256=8YND5SC_fWqv0C_bZCp0c56HU-wQCUlI1xzBY7fWHq4,49
2
+ roysh/shell.py,sha256=6MxZBS0PJvN5wxxtgb8gLr8RmDrDmtHwZ_y1qqVzYtU,10667
3
+ roysh-0.1.dist-info/METADATA,sha256=JwEVOef1tke0J56Vx7zJwSrmXwxkTdQBLmWJLaZAmGk,2306
4
+ roysh-0.1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
5
+ roysh-0.1.dist-info/entry_points.txt,sha256=ujz21BagQ6boNinTjOmAxCypk9ShJJW6NimV0Ukh9IY,37
6
+ roysh-0.1.dist-info/top_level.txt,sha256=g9yff3iyVU5f3JdBFdMYcIcKLRPL7iMCukSbrGmdA3A,6
7
+ roysh-0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ roysh = roysh:main
@@ -0,0 +1 @@
1
+ roysh