TTY OV  1
A cross platform python terminal
Loading...
Searching...
No Matches
tty_ov.py
Go to the documentation of this file.
1"""
2# +==== BEGIN tty_ov =================+
3# LOGO:
4# ..@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
5# .@...........................#@
6# @############################.@
7# @...........................@.@
8# @..#######################..@.@
9# @.#########################.@.@
10# @.##>_#####################.@.@
11# @.#########################.@.@
12# @.#########################.@.@
13# @.#########################.@.@
14# @.#########################.@.@
15# @..#######################..@.@
16# @...........................@.@
17# @..+----+______________.....@.@
18# @..+....+______________+....@.@
19# @..+----+...................@.@
20# @...........................@.#
21# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@#.
22# /STOP
23# PROJECT: tty_ov
24# FILE: tty_ov.py
25# CREATION DATE: 11-02-2026
26# LAST Modified: 19:38:51 11-02-2026
27# DESCRIPTION:
28# A module that emulates a few core functionalities of a tty (see the inner help for a list of functions).
29# /STOP
30# COPYRIGHT: (c) Henry Letellier
31# PURPOSE: This is the main implementation of the terminal and it's interraction, was coded in a week so the structure differs from my other python modules or more recent code.
32# // AR
33# +==== END tty_ov =================+
34"""
35import os
36import sys
37import shutil
38from typing import List, Dict
39import prompt_toolkit
40from prompt_toolkit.key_binding import KeyBindings
41from prompt_toolkit.history import InMemoryHistory
42from ask_question import AskQuestion
43from colourise_output import ColouriseOutput
44from .hl_ls import HLLs
45
46
47class TTY:
48 """ The class in charge of simulating a tty """
49
50 def __init__(self, err: int, error: int, success: int, colour_lib: ColouriseOutput, ask_question: AskQuestion, colours: Dict, colourise_output: bool = True) -> None:
51 # ---- The version of the program ----
52 self.__version__ = "1.0.0"
53 # ---- TTY general info ----
55 self.client_name = "(c) OpenValue"
56 self.program_author = "(c) Henry Letellier"
57 # ---- Command history ----
58 self.history = []
59 self.prompt_history = InMemoryHistory()
61 # ---- The status codes ----
62 self.success = success
63 self.err = err
64 self.error = error
65 # ---- TTY layer tracking ----
68 # ---- The dependency libraries ----
69 self.colour_lib = colour_lib
70 self.ask_question = ask_question
71 # ---- The commands of the layer in which the TTY is currently located ----
73 # ---- Available TTY colours ----
74 self.colours = self.colour_lib.unix_colour_pallet
75 self.tty_colours = colours
76 # ---- Pre-set TTY colours ----
77 self.reset_colour = None
78 self.prompt_colour = None
79 self.default_colour = None
80 self.error_colour = None
81 self.success_colour = None
82 self.info_colour = None
83 # ---- User input tracking ----
84 self.user_input = ""
85 self.user_session = prompt_toolkit.PromptSession()
87 # ---- function requirering help from the help function ----
90 "help", "man", ".help",
91 ".h", "/?", "-h",
92 "--h", "-help", "--help"
93 ]
95 # ---- Help colour ----
99 # ---- Colour toggle ----
100 self.colourise_outputcolourise_output = colourise_output
101 # ---- Environement variables colours ----
102 self.env_term_colour = None
105 # ---- Session Name ----
106 self.session_name = "main"
108 # ---- cd management ----
109 self.old_pwd = os.getcwd()
110 self.home = None
111 # ---- A tiny ls implementation ----
112 self.ls = HLLs(self.success, self.error)
113 # ---- Master session name ----
114 self.master_session = "main"
115 # ---- Argument command tracking ----
117 # ---- Comment Tracking ----
118 self.comment_token = "--"
119 # ---- Pipe input ----
120 self.pipe_input = None
121 # ---- Working on the auto-complete functionalities ----
126 # ---- TTY command description token inner ----
128
129 def print_on_tty(self, colour: str, string: str) -> None:
130 """ The function in charge of displaying a string on the tty """
132 self.colour_lib.display(colour, (), string)
133 else:
134 print(string, end="")
135
136 def run_external_command(self, command: str) -> int:
137 """ The function in charge of executing command on the host system in a contained manner """
138 try:
139 return os.system(command)
140 except IOError:
141 return self.error
142
143 def list_to_str(self, hl_list: List[any], join: str = " ") -> str:
144 """ Convert a list to a string """
145 res = ""
146 list_length = len(hl_list)-1
147 for index, item in enumerate(hl_list):
148 res += str(item)
149 if index < list_length:
150 res += join
151 return res
152
153 def version(self, args: List) -> int:
154 """ Display the version of the program """
155 func_name = "version"
156 if self.help_function_child_name == func_name:
157 help_description = f"""
158Display the version of the program.
159Usage Example:
160Input:
161 {func_name}
162Output:
163 {self.program_version}
164"""
165 self.function_help(func_name, help_description)
167 return self.success
168 self.print_on_tty(self.default_colour, "The program's version is: ")
169 self.print_on_tty(self.success_colour, f"{self.program_version}\n")
171 return self.success
172
173 def show_history(self, args: List) -> int:
174 """ Display the history of the commands """
175 func_name = "history"
176 if self.help_function_child_name == func_name:
177 help_description = f"""
178Display the history of the commands.
179Usage Example:
180Input:
181 {func_name}
182Output:
183 The history of the commands
184"""
185 self.function_help(func_name, help_description)
187 return self.success
188 self.print_on_tty(
189 self.default_colour,
190 "The history of the commands:\n"
191 )
192 if len(self.history) > 0:
193 for index, command in enumerate(self.history):
194 self.print_on_tty(self.help_command_colour, f"{index}")
195 self.print_on_tty(self.help_title_colour, ": ")
196 self.print_on_tty(
197 self.help_description_colour, f"'{command}'\n")
198 else:
199 self.print_on_tty(self.error_colour, "No history available\n")
201 return self.success
202
203 def process_session_name(self, args: List) -> int:
204 """ Change the name of the current session """
205 func_name = "session_name"
206 if self.help_function_child_name == func_name:
207 help_description = f"""
208Change the name of the current session.
209If no arguments are passed, the name of the session is displayed
210If one argument is passed, the name of the session is changed to the passed in argument
211Usage Example:
212Input:
213 {func_name}
214Output:
215 The name of the session is: {self.session_name}
216Input:
217 {func_name} test
218Output:
219 The name of the session is changed to: 'test'
220"""
221 self.function_help(func_name, help_description)
223 return self.success
224 if len(args) == 0 or args[0] == '':
225 self.print_on_tty(
226 self.default_colour,
227 "The name of the session is: "
228 )
229 self.print_on_tty(self.success_colour, f"'{self.session_name}'\n")
231 return self.success
232 self.session_name = self.list_to_str(args)
233 self.print_on_tty(
234 self.default_colour,
235 "The name of the session is changed to: "
236 )
237 self.print_on_tty(self.success_colour, f"'{self.session_name}'\n")
239 return self.success
240
241 def author(self, args: List) -> int:
242 """ Display the author of the program """
243 func_name = "author"
244 if self.help_function_child_name == func_name:
245 help_description = f"""
246Display the author of the program.
247Usage Example:
248Input:
249 {func_name}
250Output:
251 {self.program_author}
252"""
253 self.function_help(func_name, help_description)
255 return self.success
256 self.print_on_tty(self.default_colour, "The program's author is: ")
257 self.print_on_tty(self.success_colour, f"{self.program_author}\n")
259 return self.success
260
261 def client(self, args: List) -> int:
262 """ Display the client of the program """
263 func_name = "client"
264 if self.help_function_child_name == func_name:
265 help_description = f"""
266Display the client of the program.
267Usage Example:
268Input:
269 {func_name}
270Output:
271 This program was created for: {self.client_name}
272"""
273 self.function_help(func_name, help_description)
275 return self.success
276 self.print_on_tty(
277 self.default_colour,
278 "This program was created for: "
279 )
280 self.print_on_tty(self.success_colour, f"{self.client_name}\n")
282 return self.success
283
284 def env(self, args: List) -> int:
285 """ Display the environement variables """
286 func_name = "env"
287 if self.help_function_child_name == func_name:
288 help_description = f"""
289Display the environement variables.
290Usage Example:
291Input:
292 {func_name}
293Output:
294 The environement variables
295"""
296 self.function_help(func_name, help_description)
298 return self.success
299 for key, value in os.environ.items():
300 self.print_on_tty(self.default_colour, f"{key}: {value}\n")
302 return self.success
303
304 def session_admin(self) -> None:
305 """ Display the level of the program """
306 self.print_on_tty(
307 self.default_colour,
308 "This program has admin privileges: "
309 )
310 self.print_on_tty(self.success_colour, f"{self.is_admin()}\n")
312
313 def env_plus_plus(self, args: List) -> int:
314 """ Display the environement variables """
315 func_name = "env++"
316 if self.help_function_child_name == func_name:
317 help_description = f"""
318Display the environement variables.
319Usage Example:
320Input:
321 {func_name}
322Output:
323 The environement variables
324 # terms in one colour and definitions in another
325"""
326 self.function_help(func_name, help_description)
328 return self.success
329 for key, value in os.environ.items():
330 self.print_on_tty(self.env_term_colour, f"{key}")
331 self.print_on_tty(self.env_shell_colour, ": ")
332 self.print_on_tty(self.env_definition_colour, f"{value}")
333 self.print_on_tty(self.default_colour, "\n")
335 return self.success
336
337 def setenv(self, args: List) -> int:
338 """ Function in charge of setting a variable in the environement of the shell """
339 func_name = "setenv"
340 if self.help_function_child_name == func_name:
341 help_description = f"""
342Set an environement variable.
343If no arguments are passed, the name and value of the variable will be asked
344If one argument is passed, an empty variable is set
345If two or more argument are passed, the first argument becomes the name of the variable, the rest is compiled into a string seperated by spaces and become the value of that variable
346Usage Example:
347Input:
348 {func_name}
349Output:
350 Please enter the name of the variable: a
351 Please enter the value of the variable: b
352 Variable set
353
354Input:
355 {func_name} a
356Output:
357 Variable set
358
359Input:
360 {func_name} a b
361Output:
362 Variable set
363"""
364 self.function_help(func_name, help_description)
366 return self.success
367 self.print_on_tty(self.reset_colour, "Welcome to Setenv\n")
368 arg_length = len(args)
369 if arg_length == 1:
370 os.environ[args[0]] = ""
371 if arg_length >= 2:
372 os.environ[args[0]] = self.list_to_str(
373 args[1:],
375 )
376 if arg_length == 0:
377 var_name = self.ask_question.ask_question(
378 "Please enter the name of the variable: ",
379 "str"
380 )
381 var_value = input("Please enter the value of the variable: ")
382 os.environ[var_name] = var_value
383 self.print_on_tty(self.default_colour, "Variable set\n")
385 return self.success
386
387 def unset_single_variable(self, argument: str) -> int:
388 """ Unset a single variable """
389 if argument in os.environ:
390 del os.environ[argument]
391 return self.success
392 return self.error
393
394 def ask_for_env_to_unset(self) -> int:
395 """ Ask for the variable that needs to be removed from the environement """
396 var_name = self.ask_question.ask_question(
397 "Please enter the name of the variable: ",
398 "ascii"
399 )
400 if var_name in os.environ:
401 if self.unset_single_variable(var_name) != self.success:
402 self.print_on_tty(
403 self.error_colour,
404 f"Variable '{var_name}' does not exist\n"
405 )
407 return self.err
408 self.print_on_tty(
409 self.success_colour,
410 "Variable unset\n"
411 )
413 return self.success
414 self.print_on_tty(
415 self.error_colour,
416 f"Variable '{var_name}' does not exist\n"
417 )
419 return self.err
420
421 def unsetenv(self, args: List) -> int:
422 """ Function in charge of unsetting a variable in the environement of the shell """
423 func_name = "unsetenv"
424 if self.help_function_child_name == func_name:
425 help_description = f"""
426Unset an environement variable.
427If no arguments are passed, the name of the variable will be asked
428If one or more argument are passed, the variable with that name is unset
429If '*' is passed, the environement will be flushed
430Usage Example:
431Input:
432 {func_name}
433Output:
434 Please enter the name of the variable: a
435 Variable unset
436Input (a does not exist):
437 {func_name} a
438Output:
439 Variable 'a' does not exist
440Input:
441 {func_name} a b
442Output:
443 Variables ['a','b'] unset
444Input (a does not exist):
445 {func_name} a b
446Output:
447 Variable 'a' does not exist
448 Variables ['b'] unset
449Input:
450 {func_name} *
451Output:
452 Environement flushed
453"""
454 self.function_help(func_name, help_description)
456 return self.success
457 self.print_on_tty(self.reset_colour, "Welcome to Unsetenv\n")
458 arg_length = len(args)
459 if arg_length == 0:
460 return self.ask_for_env_to_unset()
461 if arg_length == 1:
462 if args[0] == "*":
463 os.environ.clear()
464 self.print_on_tty(
465 self.default_colour,
466 "Environement flushed\n"
467 )
469 return self.success
470 status = self.unset_single_variable(args[0])
472 if status == self.success:
473 self.print_on_tty(
474 self.default_colour,
475 "Variable unset\n"
476 )
477 return status
478 self.print_on_tty(
479 self.error_colour,
480 f"Variable '{args[0]}' does not exist\n"
481 )
482 return status
483 if arg_length > 1:
484 unset_variables = []
485 global_status = self.success
486 for arg in args:
487 status = self.unset_single_variable(arg)
488 if status != self.success:
489 global_status = status
490 self.print_on_tty(
491 self.error_colour,
492 f"Variable '{arg}' does not exist\n"
493 )
494 else:
495 unset_variables.append(arg)
496 self.print_on_tty(
497 self.success_colour,
498 f"Variables {unset_variables} unset\n"
499 )
501 return global_status
503 return self.success
504
505 def display_status_code(self, args: List) -> None:
506 """ Display the status code of the last function """
507 func_name = "?"
508 if self.help_function_child_name == func_name:
509 help_description = f"""
510Display the return code of the last function that was called.
511Usage Example:
512Input:
513 {func_name}
514Output (if return code is successefull):
515 The status is: 0
516 This status corresponds to a success
517Output (if the return code is a failure):
518 The status is: 84
519 This status corresponds to and error
520Output (if the return code is unknown [here: 1]):
521 The status is: 1
522 This status is not referenced by this terminal
523"""
524 self.function_help(func_name, help_description)
526 return self.success
527 self.print_on_tty(
528 self.default_colour,
529 f"The status is: {self.current_tty_status}\n"
530 )
532 self.print_on_tty(
533 self.default_colour,
534 "This status corresponds to and error.\n"
535 )
537 self.print_on_tty(
538 self.default_colour,
539 "This status corresponds to a success.\n"
540 )
541 else:
542 self.print_on_tty(
543 self.default_colour,
544 "This status is not referenced by this terminal.\n"
545 )
547 self.print_on_tty(
548 self.default_colour,
549 "This status generally means that an error has occurred during the execution of a program\n"
550 )
552 return self.success
553
554 def function_help(self, function_name: str, description: str) -> None:
555 """ The function in charge of displaying the help for a specific function """
556 self.print_on_tty(self.help_title_colour, "Displaying help about: '")
557 self.print_on_tty(self.help_command_colour, function_name)
558 self.print_on_tty(self.help_title_colour, "'\n")
559 self.print_on_tty(self.help_description_colour, description)
560
561 def help_help(self) -> None:
562 """ Display the help on the help function """
563 func_name = "help"
564 help_description = f"""
565Display the help section.
566If no arguments are passed, the available commands are displayed
567If a command is passed in 'help' the help about that command is displayed
568If 'help' is passed in 'help' this section is displayed
569Usage Example:
570Input:
571 {func_name} hello_world
572Output:
573 The {func_name} section of the hello_world function
574
575Input:
576 {func_name} prompt
577Output:
578 The help section of the prompt function (the line asking for your command)
579"""
580 self.function_help(func_name, help_description)
582
583 def help_prompt(self) -> None:
584 """ Display the help on the prompt function """
585 prompt_description = """
586Displays the return code of the previous command.
587Here are the different status colours:
588"""
589 self.function_help("prompt", prompt_description)
590 self.print_on_tty(self.help_description_colour, "Success: ")
593 self.print_on_tty(self.help_description_colour, "\nError: ")
596 self.print_on_tty(
598 "\nUnreferenced status: "
599 )
602 self.print_on_tty(
604 "\nYou can display the status code by entering: '?'\n"
605 )
607
608 def process_help_call(self, args: List) -> int:
609 """ Process the inputs for the help calls """
610 usr_input = args[0].lower()
611 if usr_input in self.help_help_options:
612 self.help_help()
614 return self.success
615 if usr_input == "prompt":
616 self.help_prompt()
619 for item in self.optionsoptions:
620 if usr_input == list(item)[0]:
621 self.help_function_child_name = usr_input
622 item[usr_input](args[1:])
625 self.print_on_tty(
626 self.error_colour,
627 f"Invalid option: {str(args[0])}\n"
628 )
631
632 def help(self, args: List) -> int:
633 """ The help function in charge of displaying the available options to the user """
634 argsc = len(args)
635 if argsc > 0 and args[0] != '':
636 if argsc > 1 and self.enable_multi_command_help is True:
637 global_status = self.success
638 for i in enumerate(args):
639 status = self.process_help_call(args[i[0]:])
640 if status != self.success:
641 global_status = self.error
642 else:
643 global_status = self.process_help_call(args)
645 return global_status
646 self.print_on_tty(self.reset_colour, "Available commands:\n")
647 if self.optionsoptions is None:
648 self.print_on_tty(self.reset_colour, "No commands available")
651 for items in self.optionsoptions:
652 for option in items:
653 self.print_on_tty(self.env_term_colour, option)
654 self.print_on_tty(self.env_shell_colour, ": ")
655 self.print_on_tty(
657 items["desc"]
658 )
659 self.print_on_tty(self.env_definition_colour, "\n")
660 break
661 self.print_on_tty(self.default_colour, "\n")
664
665 def pwd(self, args: List) -> int:
666 """ The function in charge of displaying the current working directory """
667 func_name = "pwd"
668 if self.help_function_child_name == func_name:
669 help_description = f"""
670Display the current working directory.
671Usage Example:
672Input:
673 {func_name}
674Output:
675 The current working directory
676"""
677 self.function_help(func_name, help_description)
679 return self.success
680 self.print_on_tty(self.default_colour, f"{os.getcwd()}\n")
682 return self.success
683
684 def sanitize_directory_path(self, dir_name: str) -> str:
685 """ Replace characters that could break the creation by """
686 dir_name = dir_name.replace("\\", "/")
687 illegal_character = [
688 "\t", "\n", "\r", "\v", "\f", "\b", "\a", "\0", "\'", "\"",
689 "?", "*", "<", ">", "|", ":", ";", "!", "@", "#", "$", "%", "^",
690 "&", "(", ")", "[", "]", "{", "}", "`", "~", "=", "+"
691 ]
692 for i in illegal_character:
693 if i in dir_name:
694 dir_name = dir_name.replace(i, " ")
695 return dir_name
696
697 def create_directories(self, path: str, show_if_created: bool = True) -> int:
698 """ Create the required directories """
699 path = self.sanitize_directory_path(path)
700 if os.path.isfile(path):
701 self.print_on_tty(
702 self.error_colour,
703 f"Directory '{path}' is a file\n"
704 )
706 return self.error
707 if os.path.isdir(path):
708 self.print_on_tty(
709 self.error_colour,
710 f"Directory '{path}' already exists\n"
711 )
713 return self.error
714 try:
715 os.makedirs(path, exist_ok=True)
716 except OSError as err:
717 self.print_on_tty(
718 self.error_colour,
719 f"Directory '{path}' could not be created\n{err}"
720 )
722 return self.error
723 if show_if_created:
724 self.print_on_tty(
725 self.success_colour,
726 f"Directory '{path}' created\n"
727 )
729 return self.success
730
731 def make_directory(self, args: List) -> int:
732 """ Create a directory """
733 func_name = "mkdir"
734 if self.help_function_child_name == func_name:
735 help_description = f"""
736Create a directory.
737If no arguments are passed, the name of the directory will be asked
738If one or more argument are passed, the directory with that name is created
739Usage Example:
740Input:
741 {func_name}
742Output:
743 Please enter the name of the directory: a
744 Directory created
745Input:
746 {func_name} a
747Output:
748 Directory created
749Input:
750 {func_name} a b
751Output:
752 Directories ['a','b'] created
753Input:
754 {func_name} a/b
755Output:
756 Directory created
757Input:
758 {func_name} a/b c/d
759Output:
760 Directories ['a/b','c/d'] created
761Input (a already exists):
762 {func_name} a
763Output:
764 Directory 'a' already exists
765Input:
766 {func_name} a/b c/d
767Output:
768 Directory 'a/b' already exists
769"""
770 self.function_help(func_name, help_description)
772 return self.success
773 self.print_on_tty(self.reset_colour, "Welcome to 'mkdir'\n")
774 arg_length = len(args)
775 if arg_length == 0:
776 dir_name = self.ask_question.ask_question(
777 "Please enter the name of the directory: ",
778 "ascii"
779 )
780 self.create_directories(dir_name, True)
781 if arg_length >= 1:
782 global_status = self.success
783 created_directories = []
784 for arg in args:
785 status = self.create_directories(arg, False)
786 if status != self.success:
787 global_status = status
788 else:
789 created_directories.append(arg)
790 self.print_on_tty(
791 self.success_colour,
792 f"Directories {created_directories} created\n"
793 )
795 return global_status
797 return self.success
798
799 def check_file_path(self, file_path: str) -> int:
800 """ Check the path leading to the file to make sure the path exists """
801 if os.path.exists(file_path):
802 return self.success
803 return self.error
804
805 def create_a_file(self, filename: str) -> int:
806 """ Create a file based on the name """
807 filename = filename.replace("\"", " ")
808 filename = filename.replace("\\", "/")
809 filename_display = filename.split("/")
810 file_path = filename_display[:-1]
811 file_path = self.list_to_str(file_path, "/")
812 filename_display = filename_display[-1]
813 if "/" in filename and self.check_file_path(file_path) != self.success:
814 self.print_on_tty(
815 self.error_colour,
816 f"Path '{file_path}' does not exist, file '{filename_display}' creation failed."
817 )
820 if os.path.isdir(filename):
821 self.print_on_tty(
822 self.error_colour,
823 f"File '{filename}' is a directory\n"
824 )
826 return self.error
827 if os.path.isfile(filename):
828 self.print_on_tty(
829 self.error_colour,
830 f"File '{filename}' already exists\n"
831 )
833 return self.error
834 try:
835 with open(filename, "w", encoding="utf-8", newline="\n") as file:
836 file.write("")
837 file.close()
838 except IOError as err:
839 self.print_on_tty(
840 self.error_colour,
841 f"File '{filename}' could not be created\n{err}"
842 )
844 return self.error
846 return self.success
847
848 def touch(self, arg: List) -> int:
849 """ Create a file in the present path """
850 func_name = "touch"
851 if self.help_function_child_name == func_name:
852 help_description = f"""
853Create a file.
854If no arguments are passed, the name of the file will be asked
855If one or more argument are passed, the file with that name is created
856Usage Example:
857Input:
858 {func_name}
859Output:
860 Please enter the name of the file: a
861 File created
862Input:
863 {func_name} a
864Output:
865 File created
866Input:
867 {func_name} a b
868Output:
869 Files ['a','b'] created
870Input:
871 {func_name} a/b
872Output:
873 File created
874Input:
875 {func_name} a/b c/d
876Output:
877 Files ['a/b','c/d'] created
878Input (a already exists):
879 {func_name} a
880Output:
881 File 'a' already exists
882Input (b already exists):
883 {func_name} a/b c/d
884Output:
885 File 'a/b' already exists
886Input (the path to b does not exist):
887 {func_name} not/a/path/b c/d
888Output:
889 Path 'not/a/path/' does not exist, file 'b' creation failed.
890 Files ['c/d'] created
891"""
892 self.function_help(func_name, help_description)
894 return self.success
895 self.print_on_tty(self.default_colour, "Welcome to Touch\n")
896 arg_length = len(arg)
897 if arg_length == 0:
898 file_name = self.ask_question.ask_question(
899 "Please enter the name of the file: ",
900 "ascii"
901 )
902 return self.create_a_file(file_name)
903 if arg_length >= 1:
904 global_status = self.success
905 created_files = []
906 for file in arg:
907 status = self.create_a_file(file)
908 if status != self.success:
909 global_status = status
910 else:
911 created_files.append(file)
912 self.print_on_tty(
913 self.success_colour,
914 f"Files {created_files} created\n"
915 )
917 return global_status
919 return self.success
920
921 def remove_a_directory(self, directory_path: str) -> int:
922 """ Remove a directory (child function)"""
923 directory_path = directory_path.replace("\\", "/")
924 if os.path.isdir(directory_path):
925 shutil.rmtree(directory_path)
926 return self.success
927 return self.error
928
929 def remove_directory(self, args: List) -> int:
930 """ Remove a directory """
931 func_name = "rmdir"
932 if self.help_function_child_name == func_name:
933 help_description = f"""
934Remove a directory.
935If no arguments are passed, the name of the directory will be asked
936If one or more argument are passed, the directory with that name is removed
937Usage Example:
938Input:
939 {func_name}
940Output:
941 Please enter the name of the directory: a
942 Directory removed
943Input:
944 {func_name} a
945Output:
946 Directory removed
947Input:
948 {func_name} a b
949Output:
950 Directories ['a','b'] removed
951Input:
952 {func_name} a/b
953Output:
954 Directory removed
955Input:
956 {func_name} a/b c/d
957Output:
958 Directories ['a/b','c/d'] removed
959Input (a does not exist):
960 {func_name} a
961Output:
962 Directory 'a' does not exist
963Input (b does not exist):
964 {func_name} a/b c/d
965Output:
966 Directory 'a/b' does not exist
967 Directories ['c/d'] removed
968"""
969 self.function_help(func_name, help_description)
971 return self.success
972 self.print_on_tty(self.default_colour, "Welcome to 'rmdir'\n")
973 arg_length = len(args)
974 if arg_length == 0:
975 dir_name = self.ask_question.ask_question(
976 "Please enter the name of the directory: ",
977 "ascii"
978 )
979 return self.remove_directory([dir_name])
980 if arg_length >= 1:
981 global_status = self.success
982 removed_directories = []
983 for directory in args:
984 status = self.remove_a_directory(directory)
985 if status != self.success:
986 global_status = status
987 else:
988 removed_directories.append(directory)
989 self.print_on_tty(
990 self.success_colour,
991 f"Directories {removed_directories} removed\n"
992 )
994 return global_status
996 return self.success
997
998 def remove_an_item(self, path: str) -> int:
999 """ Remove an item """
1000 try:
1001 if os.path.isfile(path):
1002 os.remove(path)
1003 else:
1004 response = self.ask_question.ask_question(
1005 f"Are you sure you wish to remove folder {path} and all it's content? [(Y)es/(N)o]",
1006 "bool"
1007 )
1008 if response:
1009 shutil.rmtree(path)
1010 else:
1011 self.print_on_tty(self.error_colour, "Folder skipped\n")
1014 return self.success
1015 except IOError as err:
1016 self.print_on_tty(
1017 self.error_colour,
1018 f"File or directory '{path}' does not exist\n{err}\n"
1019 )
1021 return self.error
1022
1023 def remove_file(self, args: List) -> int:
1024 """ Remove a file or a directory """
1025 func_name = "rm"
1026 if self.help_function_child_name == func_name:
1027 help_description = f"""
1028Remove a file or a directory.
1029If no arguments are passed, the name of the file or directory will be asked
1030If one or more argument are passed, the file or directory with that name is removed
1031Usage Example:
1032Input:
1033 {func_name}
1034Output:
1035 Please enter the name of the file or directory: a
1036 File or directory removed
1037Input:
1038 {func_name} a
1039Output:
1040 File or directory removed
1041Input:
1042 {func_name} a b
1043Output:
1044 Files or directories ['a','b'] removed
1045Input:
1046 {func_name} a/b
1047Output:
1048 File or directory removed
1049Input:
1050 {func_name} a/b c/d
1051Output:
1052 Files or directories ['a/b','c/d'] removed
1053Input (a does not exist):
1054 {func_name} a
1055Output:
1056 File or directory 'a' does not exist
1057Input (b does not exist):
1058 {func_name} a/b c/d
1059Output:
1060 File or directory 'a/b' does not exist
1061 Files or directories ['c/d'] removed
1062"""
1063 self.function_help(func_name, help_description)
1065 return self.success
1066 self.print_on_tty(self.default_colour, "Welcome to 'rm'\n")
1067 arg_length = len(args)
1068 if arg_length == 0:
1069 file_name = self.ask_question.ask_question(
1070 "Please enter the name of the file or directory: ",
1071 "ascii"
1072 )
1073 return self.remove_an_item([file_name])
1074 if arg_length >= 1:
1075 global_status = self.success
1076 removed_files = []
1077 for file in args:
1078 status = self.remove_an_item(file)
1079 if status != self.success:
1080 global_status = status
1081 else:
1082 removed_files.append(file)
1083 self.print_on_tty(
1084 self.success_colour,
1085 f"Files or directories {removed_files} removed\n"
1086 )
1088 return global_status
1091
1092 def cd_access_directory(self, path: str) -> int:
1093 """ Access a directory based on the provided path """
1094 try:
1095 self.old_pwd = os.getcwd()
1096 os.chdir(path)
1098 return self.success
1099 except IOError:
1100 self.print_on_tty(self.error_colour, f"Invalid path: {str(path)}")
1102 return self.error
1103
1104 def cd_go_further_than_home(self, path: str) -> int:
1105 """ The function in charge of changing the current working directory to a directory further than the home directory """
1106 tmp = os.getcwd()
1107 if path[0] == "~":
1108 self.cd_access_directory(self.home)
1109 path = path[1:]
1110 if path != "" and path[0] in ("/", "\\"):
1111 path = path[1:]
1112 self.cd_access_directory(path)
1113 self.old_pwd = tmp
1115 return self.success
1116
1117 def cd_rollback(self, path: str) -> int:
1118 """ The function in charge of rolling back the current working directory to the previous one """
1119 tmp = os.getcwd()
1120 os.chdir(self.old_pwd)
1121 self.print_on_tty(self.success_colour, f"{self.old_pwd}\n")
1122 self.old_pwd = tmp
1123 if len(path) > 2:
1124 self.change_directory(path[2:])
1126 return self.success
1127
1128 def change_directory(self, args: List) -> int:
1129 """ The function in charge of changing the current working directory """
1130 func_name = "cd"
1131 if self.help_function_child_name == func_name:
1132 help_description = f"""
1133Change the current working directory.
1134If no arguments are passed, the current working directory is changed to the home directory
1135If one argument is passed, the current working directory is changed to the passed in argument
1136Usage Example:
1137Input:
1138 {func_name}
1139Output:
1140 # The current working directory is changed to the home directory
1141Input:
1142 {func_name} /home
1143Output:
1144 # The current working directory is changed to /home
1145Error handling:
1146Input:
1147 {func_name} /home/does_not_exist
1148Output:
1149 Invalid path
1150Input:
1151 {func_name} /a/path /another/path
1152Output:
1153 Invalid number of arguments
1154"""
1155 self.function_help(func_name, help_description)
1157 return self.success
1158 if len(args) == 0:
1159 return self.cd_access_directory(self.home)
1160 if len(args) == 1:
1161 if args[0][0] == "-":
1162 return self.cd_rollback(args[0])
1163 if args[0][0] == "~":
1164 return self.cd_go_further_than_home(args[0])
1165 self.cd_access_directory(args[0])
1167 return self.success
1168 self.print_on_tty(self.error_colour, "Invalid number of arguments")
1170 return self.error
1171
1172 def exit(self, args: List) -> int:
1173 """ The function in charge of exiting the layer in which the user is located """
1174 func_name = "exit"
1175 if self.help_function_child_name == func_name:
1176 help_description = f"""
1177Close this menu and return to the parent session
1178Usage Example:
1179Input:
1180 {func_name}
1181Output:
1182 The prompt from the parent
1183"""
1184 self.function_help(func_name, help_description)
1186 return self.success
1187 if self.session_name != self.master_session:
1188 self.print_on_tty(self.default_colour, "Leaving child session\n")
1189 else:
1190 self.print_on_tty(self.default_colour, "Leaving program\n")
1191 self.continue_tty_loop = False
1193
1194 def kill(self, args: List) -> int:
1195 """ The function in charge of abruptly stopping the program (not recommended) """
1196 func_name = "abort"
1197 if self.help_function_child_name == func_name:
1198 help_description = f"""
1199Exit the program instantly and abruptly.
1200This will close all child processes and will not free the allocated ressources.
1201Usage Example:
1202Input:
1203 {func_name}
1204Output:
1205 Whatever was used to launch this program.
1206"""
1207 self.function_help(func_name, help_description)
1209 return self.success
1210 self.print_on_tty(self.default_colour, "")
1212
1213 def display_status_in_prompt(self) -> None:
1214 """ Display the return code of the previous function """
1216 self.print_on_tty(self.success_colour, "~")
1218 self.print_on_tty(self.error_colour, "~")
1219 else:
1220 self.print_on_tty(self.error_colour, "~")
1221 self.print_on_tty(self.default_colour, " ")
1222
1224 """ Set up the functions in charge of changing the prompt with the content of the history command """
1225 bindings = KeyBindings()
1226
1227 @bindings.add('up')
1228 def _(event):
1229 if self.history_indexhistory_index > 0:
1231 self.user_session.default_buffer.text = self.history[self.history_indexhistory_index]
1232
1233 @bindings.add('down')
1234 def _(event):
1235 if self.history_indexhistory_index < len(self.history):
1238 self.user_session.default_buffer.reset()
1239 else:
1240 self.user_session.default_buffer.text = self.history[self.history_indexhistory_index]
1241
1242 self.user_session = prompt_toolkit.PromptSession(
1243 complete_while_typing=True,
1244 validate_while_typing=True,
1245 enable_history_search=True,
1246 key_bindings=bindings,
1247 history=self.history
1248 )
1249
1250 def process_key_inputs(self) -> str:
1251 """ act depending on the special keys pressed or if entered is pressed """
1252 try:
1253 self.user_input = self.user_session.prompt()
1254 except KeyboardInterrupt:
1255 self.user_input = ""
1256 return self.user_input
1257
1258 def display_prompt(self) -> None:
1259 """ The function in charge of displaying a basic prompt to ask the user to enter an option """
1261 self.print_on_tty(self.prompt_colour, "(")
1262 self.print_on_tty(self.session_name_colour, f"{self.session_name}")
1263 self.print_on_tty(self.prompt_colour, f") {os.getcwd()}>")
1264 self.user_input = self.process_key_inputs()
1265
1266 def get_current_folder(self) -> str:
1267 """ Return the current folder """
1268 path = os.getcwd()
1269 path = path.replace("\\", "/")
1270
1271 def bind_ls(self, args: List) -> int:
1272 """ Bind the ls function to the ls command """
1273 func_name = "ls"
1274 if self.help_function_child_name in (func_name, "dir"):
1275 help_description = f"""
1276Display the content of the current working directory.
1277Usage Example:
1278Input:
1279 {self.help_function_child_name}
1280Output:
1281 The content of the current working directory
1282"""
1283 self.function_help(self.help_function_child_name, help_description)
1285 return self.success
1286 if len(args) >= 1:
1287 status = self.ls.ls(args[0])
1289 return status
1290 status = self.ls.ls(".")
1292 return status
1293
1294 def hello_world(self, args: List) -> int:
1295 """ This is a function in charge of displaying a Hello World and the passed arguments """
1296 func_name = "hello_world"
1297 if self.help_function_child_name == func_name:
1298 help_description = f"""
1299Display a 'Hello World !'.
1300If no arguments are passed, a 'Hello World !' is displayed
1301If a command is passed, 'Hello World !' is displayed as well as the passed in arguments
1302Usage Example:
1303Input:
1304 {func_name} hi
1305Output:
1306 Hello World !
1307 0: 'hi'
1308"""
1309 self.function_help(func_name, help_description)
1311 return self.success
1312 self.print_on_tty(self.default_colour, "Hello World !\n")
1313 for index, arg in enumerate(args):
1314 self.print_on_tty(self.default_colour, f"{index}: '{arg}'\n")
1316 return self.success
1317
1318 def run_command(self, args: List) -> int:
1319 """ Run a command in the host's shell environement """
1320 help_command = "run"
1321 if self.help_function_child_name == help_command:
1322 help_description = f"""
1323This is a command that allows you to run a command on the parent shell.
1324Input:
1325 {help_command} <your command>
1326Output:
1327 The result of the command you ran.
1328Example:
1329Input:
1330 {help_command} echo "Hello World!"
1331Output:
1332 Hello World!
1333"""
1334 self.function_help(help_command, help_description)
1336 return self.success
1337 if len(args) < 1:
1338 self.print_on_tty(
1339 self.error_colour,
1340 "You need to specify a command to run\n"
1341 )
1343 return self.error
1344 command = " ".join(args)
1345 self.print_on_tty(
1346 self.default_colour,
1347 f"Running command: {command}\n"
1348 )
1349 status = self.run_external_command(command)
1350 if status != self.success:
1351 self.print_on_tty(
1352 self.error_colour,
1353 "Error while running command\n"
1354 )
1356 return status
1358 return self.success
1359
1361 """ Check if the current windows user has admin rights """
1362 command = """
1363:: Check if the script is running with administrative privileges
1364net session >nul 2>&1
1365if %errorLevel% == 0 (
1366 exit 2
1367) else (
1368 exit 3
1369)
1370"""
1371 status = os.system(command)
1372 if status == 2:
1373 return True
1374 return False
1375
1376 def is_admin(self) -> bool:
1377 """ Check if the user has admin rights """
1378 try:
1379 # On Windows, check if the user has administrator privileges
1380 if os.name == 'nt':
1381 return self.check_if_admin_for_windows()
1382 # On Unix-like systems, check if the user is the root user
1383 else:
1384 return os.geteuid() == 0
1385 except AttributeError:
1386 return False
1387
1388 def run_as_windows_admin(self, file: str) -> int:
1389 """ Run a powershell script as an administrator """
1390 self.print_on_tty(
1391 self.info_colour,
1392 "!! As of date, due to the complexity of running a command with elevated privileges, the execution status cannot be properly tracked !!\n"
1393 )
1394 return self.run_command(
1395 [
1396 "powershell.exe",
1397 "-ExecutionPolicy Bypass",
1398 "-Command \"",
1399 "Start-Process",
1400 "cmd",
1401 "-Verb RunAs",
1402 "-ArgumentList '",
1403 f"/c {file}'\""
1404 ]
1405 )
1406
1407 def check_admin(self, args: List) -> int:
1408 """ Check if the program has admin rights """
1409 func_name = "check_admin"
1410 if self.help_function_child_name == func_name:
1411 help_description = f"""
1412Check if the program has admin rights.
1413Usage Example:
1414Input:
1415 {func_name}
1416Output:
1417 {self.is_admin()}
1418"""
1419 self.function_help(func_name, help_description)
1421 return self.success
1422 self.print_on_tty(
1423 self.default_colour,
1424 f"{self.is_admin()}\n"
1425 )
1427 return self.success
1428
1429 def save_to_file(self, data: str, filepath: str) -> int:
1430 """ Save data to a file """
1431 self.print_on_tty(
1432 self.info_colour,
1433 f"Saving {data} to file '{filepath}'\n"
1434 )
1435 try:
1436 with open(filepath, "w", encoding="utf-8", newline="\n") as file:
1437 file.write(data)
1438 file.close()
1439 self.print_on_tty(
1440 self.success_colour,
1441 f"{data} saved to file '{filepath}'\n"
1442 )
1443 except IOError as err:
1444 self.print_on_tty(
1445 self.error_colour,
1446 f"File '{filepath}' could not be created\n{err}"
1447 )
1449 return self.error
1451 return self.success
1452
1453 def _set_file_content(self, file_path: str, content: str, mode: str = "w", encoding: str = "utf-8", newline: str = "\n") -> int:
1454 with open(file_path, mode, encoding=encoding, newline=newline) as file:
1455 file.write(content)
1456 return self.success
1457
1458 def run_as_admin(self, args: List) -> int:
1459 """ Run a command as an administrator """
1460 func_name = "run_as_admin"
1461 if self.help_function_child_name == func_name:
1462 help_description = f"""
1463Run a command as an administrator.
1464Usage Example:
1465Input:
1466 {func_name} <your command>
1467Output:
1468 The result of the command you ran.
1469Example:
1470Input:
1471 {func_name} echo "Hello World!"
1472Output:
1473 Hello World!
1474"""
1475 self.function_help(func_name, help_description)
1477 return self.success
1478 if len(args) < 1:
1479 self.print_on_tty(
1480 self.error_colour,
1481 "You need to specify a command to run\n"
1482 )
1484 return self.error
1485 command = " ".join(args)
1486 if os.name == "nt":
1487 commands = f"{os.getcwd()}\\your_code.bat"
1488 self.save_to_file(command, commands)
1489 status = self.run_as_windows_admin(command)
1490 self.remove_an_item(commands)
1491 if status != self.success:
1492 self.print_on_tty(
1493 self.error_colour,
1494 "Error while running command\n"
1495 )
1497 return status
1499 return self.success
1500 file_name = "/tmp/your_code.sh"
1501 user_input = " ".join(args)
1502 self._set_file_content(
1503 file_path=file_name,
1504 content=user_input,
1505 mode="w",
1506 encoding="utf-8",
1507 newline="\n"
1508 )
1509 new_command = [
1510 "chmod",
1511 "777",
1512 file_name,
1513 "&&",
1514 "sudo",
1515 "su",
1516 "-c",
1517 file_name
1518 ]
1519 return self.run_command(new_command)
1520
1521 def is_exactly_in_string(self, string1: str, string2: str) -> bool:
1522 """ Check if string1 is exactly in string2 """
1523 string1_length = string1
1524 string2_length = string2
1525 if string1_length != string2_length:
1526 return False
1527 for index, item in enumerate(string1):
1528 if item != string2[index]:
1529 return False
1530 return True
1531
1532 def process_input(self) -> None:
1533 """ The function in charge of processing the user input """
1534 if self.user_input == "":
1536 return
1537 self.history.append(self.user_input)
1538 cleaned_command = self.user_input.split(self.comment_token)[0]
1539 command = cleaned_command.split(self.input_split_char)
1540 args = command[1:]
1541 command = command[0].lower()
1542 was_found = False
1543 for item in self.optionsoptions:
1544 if self.is_exactly_in_string(list(item)[0], command) is True:
1545 item[command](args)
1546 was_found = True
1547 break
1548 if was_found is False:
1549 self.print_on_tty(
1550 self.error_colour,
1551 f"Invalid option: {str(command)}\n"
1552 )
1554
1555 def assing_colours(self) -> None:
1556 """ assing the colours to the variables in charge of managing the displays"""
1557 colours = {
1558 "default": "0A",
1559 "prompt": "0B",
1560 "error": "0C",
1561 "success": "01",
1562 "info": "0D",
1563 "reset": "rr",
1564 "help_title_colour": "0E",
1565 "help_command_colour": "0A",
1566 "help_description_colour": "0F",
1567 "env_term_colour": "09",
1568 "env_shell_colour": "03",
1569 "env_definition_colour": "0B",
1570 "session_name_colour": "0D"
1571 }
1572 for i in self.tty_colours:
1573 if i not in colours:
1574 self.tty_colours[i] = colours[i]
1575 self.reset_colour = self.tty_colours["reset"]
1576 self.prompt_colour = self.tty_colours["prompt"]
1577 self.default_colour = self.tty_colours["default"]
1578 self.error_colour = self.tty_colours["error"]
1579 self.success_colour = self.tty_colours["success"]
1580 self.info_colour = self.tty_colours["info"]
1581 self.help_title_colour = self.tty_colours["help_title_colour"]
1582 self.help_command_colour = self.tty_colours["help_command_colour"]
1583 self.help_description_colour = self.tty_colours["help_description_colour"]
1584 self.env_term_colour = self.tty_colours["env_term_colour"]
1585 self.env_shell_colour = self.tty_colours["env_shell_colour"]
1586 self.env_definition_colour = self.tty_colours["env_definition_colour"]
1587 self.session_name_colour = self.tty_colours["session_name_colour"]
1588
1589 def command_seperator(self, args: List) -> int:
1590 """ Display/Change the token in charge of indicating the beginning of a new command when many are put together """
1591 func_name = "command_seperator"
1592 if self.help_function_child_name == func_name:
1593 help_description = f"""
1594Display the token in charge of indicating the beginning of a new command when many are put together.
1595Usage Example:
1596Input:
1597 {func_name}
1598Output:
1599 {self.command_seperator_token}
1600
1601Input:
1602 {func_name} -
1603Output:
1604 The command seperator has be changed from '{self.command_seperator_token}' to '-'.
1605Input:
1606 {func_name} - c
1607Output:
1608 The command seperator has be changed from '{self.command_seperator_token}' to '-c'.
1609
1610"""
1611 self.function_help(func_name, help_description)
1613 return self.success
1614 if len(args) == 0:
1615 self.print_on_tty(
1616 self.default_colour,
1617 "The command seperator is: "
1618 )
1619 self.print_on_tty(
1620 self.success_colour,
1621 f"{self.command_seperator_token}\n"
1622 )
1624 return self.success
1625 prev_seperator = self.command_seperator_token
1626 self.command_seperator_token = "".join(args)
1627 if self.command_seperator_token == self.comment_token:
1628 self.print_on_tty(
1629 self.error_colour,
1630 "Error: The seperator cannot be the same as the comment token"
1631 )
1632 self.command_seperator_token = prev_seperator
1634 return self.error
1635 if len(self.command_seperator_token) == 0 or self.command_seperator_token.isspace() is True:
1636 self.print_on_tty(
1637 self.error_colour,
1638 "Error: The seperator cannot be empty or contain only blanks/tabs"
1639 )
1640 self.command_seperator_token = prev_seperator
1642 return self.error
1643 self.print_on_tty(
1644 self.success_colour,
1645 f"The command seperator has be changed from '{prev_seperator}' to '{self.command_seperator_token}'.\n"
1646 )
1648 return self.success
1649
1650 def update_comment_token(self, args: List) -> int:
1651 """ Display/Change the token in charge of indicating the beginning of a new comment """
1652 func_name = "comment_token"
1653 if self.help_function_child_name == func_name:
1654 help_description = f"""
1655Display the token in charge of indicating the beginning of a comment.
1656Usage Example:
1657Input:
1658 {func_name}
1659Output:
1660 {self.comment_token}
1661
1662Input:
1663 {func_name} -
1664Output:
1665 The comment token has be changed from '{self.comment_token}' to '-'.
1666Input:
1667 {func_name} - c
1668Output:
1669 The comment token has be changed from '{self.comment_token}' to '-c'.
1670
1671"""
1672 self.function_help(func_name, help_description)
1674 return self.success
1675 if len(args) == 0:
1676 self.print_on_tty(
1677 self.default_colour,
1678 "The comment token is: "
1679 )
1680 self.print_on_tty(
1681 self.success_colour,
1682 f"{self.comment_token}\n"
1683 )
1685 return self.success
1686 prev_token = self.comment_token
1687 self.comment_token = "".join(args)
1688 if len(self.comment_token) == 0 or self.comment_token.isspace() is True:
1689 self.print_on_tty(
1690 self.error_colour,
1691 "Error: The seperator cannot be empty or contain only blanks/tabs"
1692 )
1693 self.command_seperator_token = prev_token
1695 return self.error
1696 self.print_on_tty(
1697 self.success_colour,
1698 f"The comment token has be changed from '{prev_token}' to '{self.comment_token}'.\n"
1699 )
1701 return self.success
1702
1703 def title(self) -> None:
1704 """ The boot tile """
1705 if self.continue_tty_loop is False:
1706 return
1707 self.print_on_tty(
1708 self.default_colour,
1709 "Welcome to the DevOps deployer\n"
1710 )
1711 self.version([])
1712 self.session_admin()
1713 self.command_seperator([])
1714 self.update_comment_token([])
1715 self.process_session_name([])
1716 self.client([])
1717 self.author([])
1718 self.print_on_tty(self.default_colour, "\n")
1720
1721 def get_the_home_path(self) -> None:
1722 """ Get the path of the HOME variable based on the system """
1723 if "HOME" in os.environ:
1724 self.home = os.environ["HOME"]
1725 elif "HOMEDRIVE" in os.environ and "HOMEPATH" in os.environ:
1726 self.home = f"{os.environ['HOMEDRIVE']}{os.environ['HOMEPATH']}"
1727 elif "HOMEPATH" in os.environ:
1728 self.home = os.environ["HOMEPATH"]
1729 elif "HOMEDRIVE" in os.environ:
1730 self.home = os.environ["HOMEDRIVE"]
1731 else:
1732 self.home = os.getcwd()
1733
1734 def commands_to_auto_complete(self) -> None:
1735 """ Convert the available commands to a list so that it can be used for command auto-completion """
1736 for i in self.optionsoptions:
1737 for b in i:
1738 self.auto_complete_list.append(b)
1739
1740 def load_basics(self) -> None:
1741 """ set the values for the variables that can be configured by the user """
1742 self.old_pwd = os.getcwd()
1743 self.get_the_home_path()
1744 self.assing_colours()
1745 self.colour_lib.init_pallet()
1746 self.optionsoptions = [
1747 {
1748 "help": self.help,
1749 self.command_description_token_inner: "Display this help section"
1750 },
1751 {
1752 "man": self.help,
1753 self.command_description_token_inner: "Display this help section"
1754 },
1755 {
1756 ".help": self.help,
1757 self.command_description_token_inner: "Display this help section"
1758 },
1759 {".h": self.help, self.command_description_token_inner: "Display this help section"},
1760 {"/?": self.help, self.command_description_token_inner: "Display this help section"},
1761 {"-h": self.help, self.command_description_token_inner: "Display this help section"},
1762 {
1763 "--h": self.help,
1764 self.command_description_token_inner: "Display this help section"
1765 },
1766 {
1767 "-help": self.help,
1768 self.command_description_token_inner: "Display this help section"
1769 },
1770 {
1771 "--help": self.help,
1772 self.command_description_token_inner: "Display this help section"
1773 },
1774 {
1775 "setenv": self.setenv,
1776 self.command_description_token_inner: "Set a variable in the environement"
1777 },
1778 {
1779 "unsetenv": self.unsetenv,
1780 self.command_description_token_inner: "Remove a variable from the environement"
1781 },
1782 {
1783 "exit": self.exit,
1784 self.command_description_token_inner: "Close the current menu"
1785 },
1786 {
1787 "abort": self.kill,
1788 self.command_description_token_inner: "Exit the program (This will kill the program and any child processes)"
1789 },
1790 {
1791 "hello_world": self.hello_world,
1792 self.command_description_token_inner: "Display a Hello World"
1793 },
1794 {"env": self.env, self.command_description_token_inner: "Display the environement variables"},
1795 {
1796 "env++": self.env_plus_plus,
1797 self.command_description_token_inner: "Display the environement variables using different colours"
1798 },
1799 {
1800 "?": self.display_status_code,
1801 self.command_description_token_inner: "Display the status code of the last function called"
1802 },
1803 {
1804 "cd": self.change_directory,
1805 self.command_description_token_inner: "Change the current working directory"
1806 },
1807 {
1808 "pwd": self.pwd, self.command_description_token_inner:
1809 "Display the path to the directory in wich we are located"
1810 },
1811 {
1812 "version": self.version,
1813 self.command_description_token_inner: "Display the current version of the program"
1814 },
1815 {
1816 "author": self.author,
1817 self.command_description_token_inner: "Display the author of the program"
1818 },
1819 {
1820 "session_name": self.process_session_name,
1821 self.command_description_token_inner: "Change the name of the current session"
1822 },
1823 {
1824 "client": self.client,
1825 self.command_description_token_inner: "Display the client of the program"
1826 },
1827 {
1828 "history": self.show_history,
1829 self.command_description_token_inner: "Display the previous commands that were run"
1830 },
1831 {
1833 "List all files in the current folder"
1834 },
1835 {
1836 "dir": self.bind_ls, self.command_description_token_inner:
1837 "List all files in the current folder"
1838 },
1839 {
1840 "mkdir": self.make_directory,
1841 self.command_description_token_inner: "Create a directory in the present path"
1842 },
1843 {
1844 "touch": self.touch,
1845 self.command_description_token_inner: "Create a file in the present path"
1846 },
1847 {
1848 "rm": self.remove_file,
1849 self.command_description_token_inner: "Remove a file or directory if present in the path"
1850 },
1851 {
1852 "rmdir": self.remove_directory,
1853 self.command_description_token_inner: "Remove a directory if present in the path"
1854 },
1855 {
1856 "run": self.run_command,
1857 self.command_description_token_inner: "Run a command in the system terminal"
1858 },
1859 {
1860 "is_admin": self.check_admin,
1861 self.command_description_token_inner: "Return True if the system has elevated privileges."
1862 },
1863 {
1864 "super_run": self.run_as_admin,
1865 self.command_description_token_inner: "Run a command using elevated privileges"
1866 },
1867 {
1868 "command_seperator": self.command_seperator,
1869 self.command_description_token_inner: "Display/Change the token in charge of indicating the beginning of a new command when many are put together"
1870 },
1871 {
1872 "comment_token": self.update_comment_token,
1873 self.command_description_token_inner: "Display/Change the token in charge of indicating the beginning of a new command when many are put together"
1874 }
1875 ]
1877
1878 def unload_basics(self) -> int:
1879 """ Free the ressources that were previously allocated """
1880 self.old_pwd = ""
1881 self.home = ""
1882 self.reset_colour = None
1883 self.prompt_colour = None
1884 self.default_colour = None
1885 self.error_colour = None
1886 self.success_colour = None
1887 self.info_colour = None
1888 self.help_title_colour = None
1889 self.help_command_colour = None
1890 self.help_description_colour = None
1891 self.env_term_colour = None
1892 self.env_shell_colour = None
1893 self.env_definition_colour = None
1894 self.session_name_colour = None
1895 self.tty_colours = None
1896 self.optionsoptions = []
1897 self.auto_complete_list = []
1898 return self.colour_lib.unload_ressources()
1899
1900 def import_functions_into_shell(self, functions: List[Dict[str, any]]) -> int:
1901 """ Import functions into the shell """
1902 for function in functions:
1903 if function is None or isinstance(function, Dict) != True:
1904 continue
1905 if "desc" not in function:
1906 function["desc"] = "No description provided\n"
1907 self.optionsoptions.append(function)
1908 item = list(function)[0]
1909 self.auto_complete_list.append(item)
1910 self.print_on_tty(
1911 self.success_colour,
1912 f"Added function {item}\n"
1913 )
1915 return self.success
1916
1917 def remove_function_from_options(self, function: Dict[str, any]) -> int:
1918 """ Remove a function from the options """
1919 for function_item in self.optionsoptions:
1920 for index, item in enumerate(function_item):
1921 if item == function:
1922 self.optionsoptions.pop(index)
1923 self.auto_complete_list.pop(index)
1924 self.print_on_tty(self.success, f"Removed function {item}")
1927 self.print_on_tty(
1928 self.error_colour,
1929 f"Failed to remove function {item}"
1930 )
1933
1934 def remove_functions_from_shell(self, functions: List[Dict[str, any]]) -> int:
1935 """ Remove functions from the shell """
1936 global_status = self.success
1937 for function in functions:
1938 for item in function:
1939 status = self.remove_function_from_options(item)
1940 if status != self.success:
1941 global_status = status
1944
1945 def goodbye_message(self) -> None:
1946 """ Display a goodbye message on the exit of the main terminal """
1947 goodbye_message = "Goodbye, see you next time !\n"
1949 self.print_on_tty(self.success_colour, goodbye_message)
1951 self.print_on_tty(self.error_colour, goodbye_message)
1952 else:
1953 self.print_on_tty(self.error_colour, goodbye_message)
1954
1955 def run_complex_input(self, complex_input: List[str]) -> None:
1956 """ Run a complex input """
1957 for item in complex_input:
1958 self.user_input = item
1959 self.process_input()
1960
1961 def process_complex_input(self, usr_input: List) -> None:
1962 """ process multiple command input if provided """
1963 command_list = []
1964 buffer = ""
1965 prev = ""
1966 for item in usr_input:
1967 if self.command_seperator_token == item:
1968 command_list.append(buffer)
1969 prev = item
1970 buffer = ""
1971 continue
1972 if prev in (self.command_seperator_token, ""):
1973 buffer += f"{item}"
1974 prev = item
1975 continue
1976 if buffer != " ":
1977 buffer += f" {item}"
1978 prev = item
1979 if buffer != "":
1980 command_list.append(buffer)
1981 self.run_complex_input(command_list)
1982
1983 def process_if_arg_input(self) -> None:
1984 """ Check if the argv contains arguments input """
1985 if len(sys.argv) > 1:
1986 if self.command_seperator_token in sys.argv:
1987 self.process_complex_input(sys.argv[1:])
1988 else:
1989 self.user_input = " ".join(sys.argv[1:])
1990 self.process_input()
1991 if self.continue_tty_loop is True:
1992 self.print_on_tty(
1993 self.default_colour,
1994 "\n\n\n\n"
1995 )
1996
1997 def clean_string(self, input_string: str) -> str:
1998 """ remove enclosing string from the run string """
1999 if len(input_string) > 0:
2000 if input_string[0] == '"':
2001 input_string = input_string[1:]
2002 if input_string[-1] == '"':
2003 input_string = input_string[:-1]
2004 if input_string[-2] == '"':
2005 input_string = input_string[:-2]+input_string[-1:]
2006 if input_string[-3] == '"':
2007 input_string = input_string[:-3]+input_string[-2:]
2008 return input_string
2009
2010 def process_if_pipe_input(self) -> None:
2011 """ Check if the user input is a pipe input """
2012 if not sys.stdin.isatty():
2013 user_input = sys.stdin.read()
2014 user_input = self.clean_string(user_input)
2015 user_args = user_input.split(self.input_split_char)
2016 self.process_complex_input(user_args)
2017 if self.continue_tty_loop is True:
2018 self.exit([])
2019
2020 def mainloop(self, session_name="main") -> int:
2021 """ The mainloop allowing the terminal to run like any other terminals """
2022 self.session_name = session_name
2025 self.title()
2026 while self.continue_tty_loop is True:
2027 self.help_function_child_name = "help"
2028 self.display_prompt()
2029 seperated_commands = self.user_input.split(self.input_split_char)
2030 self.process_complex_input(seperated_commands)
2031 if self.session_name == "main":
2032 self.goodbye_message()
2033 self.print_on_tty(self.reset_colour, "")
2035
2036
2037if __name__ == "__main__":
2038 ERR = 84
2039 ERROR = ERR
2040 SUCCESS = 0
2041 COLOUR_LIB = ColouriseOutput()
2042 ASK_QUESTION = AskQuestion()
2043 CONSTANTS = {
2044 "default": "0A",
2045 "prompt": "0B",
2046 "error": "0C",
2047 "success": "03",
2048 "info": "0D",
2049 "reset": "rr",
2050 "help_title_colour": "0E",
2051 "help_command_colour": "0A",
2052 "help_description_colour": "0F",
2053 "env_term_colour": "09",
2054 "env_shell_colour": "03",
2055 "env_definition_colour": "0B",
2056 "session_name_colour": "0D"
2057 }
2058 COLOURISE_OUTPUT = True
2059 TTYI = TTY(
2060 ERR,
2061 ERROR,
2062 SUCCESS,
2063 COLOUR_LIB,
2064 ASK_QUESTION,
2065 CONSTANTS,
2066 COLOURISE_OUTPUT
2067 )
2068 TTYI.load_basics()
2069 TTYI.mainloop("Test session")
2070 TTYI.unload_basics()
None __init__(self, int err, int error, int success, ColouriseOutput colour_lib, AskQuestion ask_question, Dict colours, bool colourise_output=True)
Definition tty_ov.py:50
None goodbye_message(self)
Definition tty_ov.py:1945
int check_admin(self, List args)
Definition tty_ov.py:1407
int current_tty_status
Definition tty_ov.py:67
None print_on_tty(self, str colour, str string)
Definition tty_ov.py:129
list help_help_options
Definition tty_ov.py:89
str list_to_str(self, List[any] hl_list, str join=" ")
Definition tty_ov.py:143
int unsetenv(self, List args)
Definition tty_ov.py:421
None get_the_home_path(self)
Definition tty_ov.py:1721
str get_current_folder(self)
Definition tty_ov.py:1266
None session_admin(self)
Definition tty_ov.py:304
int _set_file_content(self, str file_path, str content, str mode="w", str encoding="utf-8", str newline="\n")
Definition tty_ov.py:1453
str program_version
Definition tty_ov.py:54
None commands_to_auto_complete(self)
Definition tty_ov.py:1734
str process_key_inputs(self)
Definition tty_ov.py:1250
None display_status_code(self, List args)
Definition tty_ov.py:505
int version(self, List args)
Definition tty_ov.py:153
int process_session_name(self, List args)
Definition tty_ov.py:203
bool is_admin(self)
Definition tty_ov.py:1376
int run_command(self, List args)
Definition tty_ov.py:1318
str input_split_char
Definition tty_ov.py:86
None help_prompt(self)
Definition tty_ov.py:583
int setenv(self, List args)
Definition tty_ov.py:337
int update_comment_token(self, List args)
Definition tty_ov.py:1650
bool enable_multi_command_help
Definition tty_ov.py:94
int client(self, List args)
Definition tty_ov.py:261
str command_description_token_inner
Definition tty_ov.py:127
int create_directories(self, str path, bool show_if_created=True)
Definition tty_ov.py:697
str auto_complete_usr_input
Definition tty_ov.py:124
int env(self, List args)
Definition tty_ov.py:284
str sanitize_directory_path(self, str dir_name)
Definition tty_ov.py:684
None process_input(self)
Definition tty_ov.py:1532
None create_key_prompt_bindings(self)
Definition tty_ov.py:1223
int kill(self, List args)
Definition tty_ov.py:1194
int import_functions_into_shell(self, List[Dict[str, any]] functions)
Definition tty_ov.py:1900
int unset_single_variable(self, str argument)
Definition tty_ov.py:387
bool is_exactly_in_string(self, str string1, str string2)
Definition tty_ov.py:1521
bool current_tty_status
Definition tty_ov.py:712
int touch(self, List arg)
Definition tty_ov.py:848
int check_file_path(self, str file_path)
Definition tty_ov.py:799
bool continue_tty_loop
Definition tty_ov.py:66
int ask_for_env_to_unset(self)
Definition tty_ov.py:394
None assing_colours(self)
Definition tty_ov.py:1555
int remove_function_from_options(self, Dict[str, any] function)
Definition tty_ov.py:1917
int show_history(self, List args)
Definition tty_ov.py:173
int author(self, List args)
Definition tty_ov.py:241
int create_a_file(self, str filename)
Definition tty_ov.py:805
int remove_file(self, List args)
Definition tty_ov.py:1023
None process_if_pipe_input(self)
Definition tty_ov.py:2010
int process_help_call(self, List args)
Definition tty_ov.py:608
bool check_if_admin_for_windows(self)
Definition tty_ov.py:1360
None process_if_arg_input(self)
Definition tty_ov.py:1983
int cd_access_directory(self, str path)
Definition tty_ov.py:1092
str auto_complete_default_usr_input
Definition tty_ov.py:125
None process_complex_input(self, List usr_input)
Definition tty_ov.py:1961
int mainloop(self, session_name="main")
Definition tty_ov.py:2020
int remove_functions_from_shell(self, List[Dict[str, any]] functions)
Definition tty_ov.py:1934
int exit(self, List args)
Definition tty_ov.py:1172
int make_directory(self, List args)
Definition tty_ov.py:731
int remove_an_item(self, str path)
Definition tty_ov.py:998
str clean_string(self, str input_string)
Definition tty_ov.py:1997
int unload_basics(self)
Definition tty_ov.py:1878
str command_seperator_token
Definition tty_ov.py:116
int run_as_admin(self, List args)
Definition tty_ov.py:1458
int command_seperator(self, List args)
Definition tty_ov.py:1589
int hello_world(self, List args)
Definition tty_ov.py:1294
int pwd(self, List args)
Definition tty_ov.py:665
int run_external_command(self, str command)
Definition tty_ov.py:136
int help(self, List args)
Definition tty_ov.py:632
int run_as_windows_admin(self, str file)
Definition tty_ov.py:1388
None display_prompt(self)
Definition tty_ov.py:1258
None function_help(self, str function_name, str description)
Definition tty_ov.py:554
None title(self)
Definition tty_ov.py:1703
str help_function_child_name
Definition tty_ov.py:88
int bind_ls(self, List args)
Definition tty_ov.py:1271
int cd_rollback(self, str path)
Definition tty_ov.py:1117
int env_plus_plus(self, List args)
Definition tty_ov.py:313
int save_to_file(self, str data, str filepath)
Definition tty_ov.py:1429
None help_help(self)
Definition tty_ov.py:561
int remove_directory(self, List args)
Definition tty_ov.py:929
int auto_complete_index
Definition tty_ov.py:123
None run_complex_input(self, List[str] complex_input)
Definition tty_ov.py:1955
None load_basics(self)
Definition tty_ov.py:1740
int change_directory(self, List args)
Definition tty_ov.py:1128
int remove_a_directory(self, str directory_path)
Definition tty_ov.py:921
int cd_go_further_than_home(self, str path)
Definition tty_ov.py:1104
None display_status_in_prompt(self)
Definition tty_ov.py:1213
list auto_complete_list
Definition tty_ov.py:122