minishell 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ Metadata-Version: 2.1
2
+ Name: minishell
3
+ Version: 0.0.1
4
+ Summary:
5
+ License: LGPL-3.0-or-later
6
+ Author: RomanLabs
7
+ Author-email: rromanotero@romanlabs.com
8
+ Requires-Python: >=3.11,<4.0
9
+ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Dist: mypy (>=1.10.0,<2.0.0)
14
+ Requires-Dist: rich (>=13.7.1,<14.0.0)
15
+ Description-Content-Type: text/markdown
16
+
17
+ ## MiniShell
18
+
19
+ [Project in Gitlab](https://gitlab.com/romanlabs/public/minishell)
20
+
21
+
@@ -0,0 +1,4 @@
1
+ ## MiniShell
2
+
3
+ [Project in Gitlab](https://gitlab.com/romanlabs/public/minishell)
4
+
@@ -0,0 +1,25 @@
1
+ [tool.poetry]
2
+ name = "minishell"
3
+ version = "0.0.1"
4
+ description = ""
5
+ authors = ["RomanLabs <rromanotero@romanlabs.com>"]
6
+ readme = "README.md"
7
+ license = "LGPL-3.0-or-later"
8
+ packages = [{include = "minishell", from = "src"}]
9
+
10
+ [tool.poetry.scripts]
11
+ manual-test-run = "minishell.manual_test_run:main"
12
+
13
+ [tool.poetry.dependencies]
14
+ python = "^3.11"
15
+ mypy = "^1.10.0"
16
+ rich = "^13.7.1"
17
+
18
+ [tool.poetry.group.dev.dependencies]
19
+ mypy = "^1.10.0"
20
+ pytest = "^8.2.1"
21
+ toml = "^0.10.2"
22
+
23
+ [build-system]
24
+ requires = ["poetry-core"]
25
+ build-backend = "poetry.core.masonry.api"
@@ -0,0 +1,21 @@
1
+ # Copyright (C) 2024 RomanLabs, Rafael Roman Otero
2
+ # This file is part of API Pipe.
3
+ #
4
+ # API Pipe is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # API Pipe is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public License
15
+ # along with API Pipe. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ '''
18
+ Module Name
19
+ '''
20
+ __version__ = '0.0.1'
21
+ __author__ = 'RomanLabs'
@@ -0,0 +1,170 @@
1
+ # Copyright (C) 2024 RomanLabs, Rafael Roman Otero
2
+ # This file is part of API Pipe.
3
+ #
4
+ # API Pipe is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # API Pipe is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public License
15
+ # along with API Pipe. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ '''
18
+ Cmd
19
+ '''
20
+ import logging
21
+ import subprocess
22
+ from typing import ClassVar
23
+
24
+ from minishell.error import ArgumentError, NonZeroExit
25
+ from minishell import logger
26
+
27
+ class CmdMeta(type):
28
+ def __getattr__(cls, method_name: str):
29
+ '''
30
+ __getattr__
31
+
32
+ Captures get attribute calls
33
+ to the Cmd class
34
+
35
+ This enables the chaining of commands
36
+ STARTING FROM Cmd, as opposed to Cmd()
37
+
38
+ e.g.
39
+
40
+ Cmd.i.am.a.command.withh.arguments("arg1")
41
+
42
+ '''
43
+ return Cmd(method_name)
44
+
45
+ class Cmd(metaclass=CmdMeta):
46
+ _log: ClassVar[logging.Logger] = logger.stdout(__name__, logging.DEBUG)
47
+ __is_first_call: ClassVar[bool] = False
48
+
49
+ @classmethod
50
+ def log_setup(cls, log_level: int):
51
+ '''
52
+ log_setup
53
+
54
+ Sets up logging for all commands
55
+ '''
56
+ Cmd._log = logger.stdout(__name__, log_level)
57
+
58
+ def __init__(self, cmd_str_to_add=''):
59
+ '''
60
+ __init__
61
+ '''
62
+ if not Cmd.__is_first_call:
63
+ # enable pretty tracebacks
64
+ logger.enable_pretty_tracebacks()
65
+ Cmd.__is_first_call = True
66
+
67
+ self.cmd_str = cmd_str_to_add
68
+
69
+ def __getattr__(self, method_name: str):
70
+ '''
71
+ __getattr__
72
+
73
+ Captures get attribute calls
74
+
75
+ Returns a new Cmd object with
76
+ the attribute name appended to cmd_str
77
+ '''
78
+ if self.cmd_str:
79
+ return Cmd(f"{self.cmd_str} {method_name}")
80
+ else:
81
+ #
82
+ # this runs only once
83
+ # when the first method is called
84
+ #
85
+ # e.g.
86
+ # Cmd().i.am.a.command.withh.arguments("arg1")
87
+ #
88
+ # calling i will return a new Cmd object
89
+ # with cmd_str set to i
90
+ #
91
+ # Therefore, the next call to am will append to cmd_str
92
+ # and so forth
93
+ #
94
+ return Cmd(method_name)
95
+
96
+ def __call__(self, *args, **kwargs):
97
+ '''
98
+ __call__
99
+
100
+ Captures call to object
101
+
102
+ This is the last call in the chain
103
+ and will execute the command with
104
+ the arguments passed in
105
+ '''
106
+ if kwargs:
107
+ raise ArgumentError(
108
+ f"Shell Command object cannot take keyword arguments"
109
+ )
110
+ return self.__execute_command(
111
+ self.__add_args(self.cmd_str, *args)
112
+ )
113
+
114
+ def __add_args(self, cmd_str: str, *args) -> str:
115
+ '''
116
+ Add arguments to command string
117
+
118
+ Returns a string with the command string
119
+ and arguments appended to it
120
+
121
+ e.g.
122
+
123
+ In:
124
+
125
+ Cmd().i.am.a.command.withh.arguments("arg1")
126
+
127
+ cmd_str = "i am a command withh arguments"
128
+ args = ("arg1",)
129
+
130
+ will return:
131
+ "i am a command withh arguments arg1"
132
+ '''
133
+ args_str = ' '.join(str(arg) for arg in args)
134
+ return f"{cmd_str} {args_str}"
135
+
136
+ def __execute_command(self, cmd_str: str) -> tuple[str, str]:
137
+ '''
138
+ Execute command in cmd_str
139
+ '''
140
+ Cmd._log.debug(cmd_str)
141
+
142
+ output = subprocess.run(
143
+ cmd_str,
144
+ capture_output=True,
145
+ shell=True, # There's more to this but the main
146
+ # reason is that shell=False will
147
+ # raise exceptions on non-zero exit codes
148
+ text=True,
149
+ check=False # Don't raise exception on non-zero exit code
150
+ )
151
+
152
+ if output.stdout:
153
+ Cmd._log.info(output.stdout)
154
+
155
+ if output.stderr:
156
+ Cmd._log.error(output.stderr)
157
+
158
+ if output.returncode != 0:
159
+ raise NonZeroExit(
160
+ cmd_str,
161
+ output.returncode,
162
+ output.stdout,
163
+ output.stderr
164
+ )
165
+
166
+ stdout = output.stdout.strip() if output.stdout else ''
167
+ stderr = output.stderr.strip() if output.stderr else ''
168
+
169
+ return stdout, stderr
170
+
@@ -0,0 +1,58 @@
1
+ # Copyright (C) 2024 RomanLabs, Rafael Roman Otero
2
+ # This file is part of API Pipe.
3
+ #
4
+ # API Pipe is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # API Pipe is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public License
15
+ # along with API Pipe. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ '''
18
+ Tool
19
+
20
+ This file is used to configure the tool
21
+ '''
22
+
23
+ #logs
24
+ # (these are Paths not strings)
25
+ log_formatter = "%(message)s"
26
+ log_show_time = False
27
+ log_words_to_highlight = [
28
+ "importing",
29
+ "exporting",
30
+ "validating",
31
+ "deleting",
32
+ "getting",
33
+ "writing",
34
+ "reading",
35
+ "found",
36
+ "making",
37
+ "removing",
38
+ "selecting",
39
+ "emptying",
40
+ "initializing",
41
+ "logging",
42
+ "checking",
43
+ "requesting",
44
+ "imported",
45
+ "collecting",
46
+ "parsing",
47
+ "loading",
48
+ "cleaning",
49
+ "fetching",
50
+ "converting",
51
+ "created",
52
+ "Initialiazing",
53
+ "rendering",
54
+ "appending",
55
+ "processing",
56
+ "creating",
57
+ "created"
58
+ ]
@@ -0,0 +1,58 @@
1
+ # Copyright (C) 2024 RomanLabs, Rafael Roman Otero
2
+ # This file is part of API Pipe.
3
+ #
4
+ # API Pipe is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # API Pipe is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public License
15
+ # along with API Pipe. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ '''
18
+ error.py
19
+ '''
20
+ from minishell import logger
21
+
22
+ log = logger.stdout(
23
+ __name__,
24
+ logger.logging.CRITICAL
25
+ )
26
+
27
+ class ArgumentError(TypeError):
28
+ '''
29
+ ArgumentError
30
+
31
+ Raised when an argument is invalid
32
+ '''
33
+ def __init__(self, msg: str):
34
+
35
+ super().__init__(msg)
36
+
37
+ class NonZeroExit(RuntimeError):
38
+ '''
39
+ CommandError
40
+
41
+ Raised when a command fails
42
+ '''
43
+ def __init__(self,
44
+ command: str,
45
+ return_code: int,
46
+ stdout: str,
47
+ stderr: str
48
+ ):
49
+ if stdout:
50
+ stdout = f"\n{stdout}"
51
+ if stderr:
52
+ stderr = f"\n{stderr}"
53
+
54
+ super().__init__(
55
+ f"Command '{command}' failed with return code {return_code}:"
56
+ f"{stdout}"
57
+ f"{stderr}"
58
+ )
@@ -0,0 +1,70 @@
1
+ # Copyright (C) 2024 RomanLabs, Rafael Roman Otero
2
+ # This file is part of API Pipe.
3
+ #
4
+ # API Pipe is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # API Pipe is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public License
15
+ # along with API Pipe. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ '''
18
+ logger.py
19
+ '''
20
+ import logging
21
+ from rich.logging import RichHandler
22
+ from rich.traceback import install
23
+
24
+ from minishell import config
25
+
26
+ def enable_pretty_tracebacks() -> None:
27
+ '''
28
+ Enable Pretty Traceback
29
+
30
+ Uses rich to print pretty tracebacks
31
+ '''
32
+ install()
33
+
34
+ def stdout(name: str, log_level: int) -> logging.Logger:
35
+ '''
36
+ Sets up a logger that logs to stdout
37
+
38
+ Uses RichHandler to pretty print logs
39
+ '''
40
+ words_to_highlight = config.log_words_to_highlight
41
+
42
+ handler = RichHandler(
43
+ show_time=config.log_show_time,
44
+ keywords=[w.lower() for w in words_to_highlight] + \
45
+ [w.upper() for w in words_to_highlight] + \
46
+ [w.capitalize() for w in words_to_highlight]
47
+ )
48
+
49
+ logger = CustomLogger(name)
50
+ logger.setLevel(log_level)
51
+ formatter = logging.Formatter(config.log_formatter)
52
+ handler.setFormatter(formatter)
53
+ logger.addHandler(handler)
54
+
55
+ return logger
56
+
57
+ class CustomLogger(logging.Logger):
58
+ '''
59
+ Extends the logging.Logger class
60
+ with custom methods
61
+ '''
62
+ def flush(self) -> None:
63
+ '''
64
+ Flush
65
+
66
+ Flushes the logger
67
+ '''
68
+ for handler in self.handlers:
69
+ handler.flush()
70
+
@@ -0,0 +1,50 @@
1
+ # Copyright (C) 2024 RomanLabs, Rafael Roman Otero
2
+ # This file is part of API Pipe.
3
+ #
4
+ # API Pipe is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Lesser General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # API Pipe is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Lesser General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU Lesser General Public License
15
+ # along with API Pipe. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ '''
18
+ Run Manual Test
19
+ (entry point)
20
+
21
+ For help type:
22
+ poetry run manual-test-run --help
23
+ '''
24
+ import logging
25
+
26
+ from minishell.cmd import Cmd
27
+ from minishell import logger
28
+
29
+ log = logger.stdout(
30
+ __name__,
31
+ logging.DEBUG
32
+ )
33
+
34
+ def main():
35
+ '''
36
+ main
37
+ '''
38
+ Cmd.date()
39
+ Cmd.uname("-s")
40
+ Cmd.ipconfig.getifaddr.en0()
41
+ Cmd().ls(
42
+ "-l",
43
+ "tests/integration/ls_test_dir/file"
44
+ )
45
+ Cmd().cat(
46
+ "tests/integration/ls_test_dir/file"
47
+ )
48
+
49
+ stdout, _ = Cmd().ipconfig.getifaddr.en0()
50
+ print("My IP Address is: ", stdout)