replbase 0.0.24__tar.gz → 0.0.26__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.
- {replbase-0.0.24 → replbase-0.0.26}/PKG-INFO +2 -1
- {replbase-0.0.24 → replbase-0.0.26}/pyproject.toml +2 -1
- {replbase-0.0.24 → replbase-0.0.26}/replbase/repl_base.py +148 -0
- {replbase-0.0.24 → replbase-0.0.26}/LICENSE +0 -0
- {replbase-0.0.24 → replbase-0.0.26}/README.md +0 -0
- {replbase-0.0.24 → replbase-0.0.26}/replbase/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: replbase
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.26
|
|
4
4
|
Summary: "Combination of other REPL tools into a reusable class that generates a REPL"
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Joseph Bochinski
|
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
12
12
|
Requires-Dist: prompt-toolkit (>=3.0.48,<4.0.0)
|
|
13
13
|
Requires-Dist: ptpython (>=3.0.29,<4.0.0)
|
|
14
14
|
Requires-Dist: rich (>=13.9.4,<14.0.0)
|
|
15
|
+
Requires-Dist: tabulate (>=0.9.0,<0.10.0)
|
|
15
16
|
Description-Content-Type: text/markdown
|
|
16
17
|
|
|
17
18
|
# replbase
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "replbase"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.26"
|
|
8
8
|
description = "\"Combination of other REPL tools into a reusable class that generates a REPL\""
|
|
9
9
|
authors = [ "Joseph Bochinski <stirgejr@gmail.com>",]
|
|
10
10
|
license = "MIT"
|
|
@@ -15,3 +15,4 @@ python = "^3.12"
|
|
|
15
15
|
prompt-toolkit = "^3.0.48"
|
|
16
16
|
ptpython = "^3.0.29"
|
|
17
17
|
rich = "^13.9.4"
|
|
18
|
+
tabulate = "^0.9.0"
|
|
@@ -15,9 +15,14 @@
|
|
|
15
15
|
from __future__ import annotations
|
|
16
16
|
|
|
17
17
|
import argparse
|
|
18
|
+
import grp
|
|
19
|
+
import math
|
|
18
20
|
import os
|
|
21
|
+
import pwd
|
|
19
22
|
import re
|
|
20
23
|
import shlex
|
|
24
|
+
import stat
|
|
25
|
+
import time
|
|
21
26
|
|
|
22
27
|
from dataclasses import dataclass, field
|
|
23
28
|
from typing import Any, Callable, Literal
|
|
@@ -30,8 +35,10 @@ from prompt_toolkit.history import FileHistory
|
|
|
30
35
|
from prompt_toolkit.completion import WordCompleter
|
|
31
36
|
from prompt_toolkit.styles import Style
|
|
32
37
|
from ptpython.repl import embed
|
|
38
|
+
from rich import pretty
|
|
33
39
|
from rich.console import Console
|
|
34
40
|
from rich.theme import Theme
|
|
41
|
+
from tabulate import tabulate
|
|
35
42
|
|
|
36
43
|
# endregion Imports
|
|
37
44
|
|
|
@@ -49,6 +56,77 @@ def is_num_str(val: str) -> bool:
|
|
|
49
56
|
return bool(re.match(r"^-?\d+(\.\d+)?$", val))
|
|
50
57
|
|
|
51
58
|
|
|
59
|
+
def ls_liah(path: str = "."):
|
|
60
|
+
"""Print output similar to using `ls -liah` in the terminal"""
|
|
61
|
+
|
|
62
|
+
files = os.listdir(path)
|
|
63
|
+
files_info = []
|
|
64
|
+
|
|
65
|
+
for file_name in files:
|
|
66
|
+
# Get the full path
|
|
67
|
+
full_path = os.path.join(path, file_name)
|
|
68
|
+
|
|
69
|
+
# Get file stats
|
|
70
|
+
file_stat = os.lstat(full_path)
|
|
71
|
+
|
|
72
|
+
# Get permissions
|
|
73
|
+
permissions = stat.filemode(file_stat.st_mode)
|
|
74
|
+
|
|
75
|
+
# Get number of hard links
|
|
76
|
+
hard_links = file_stat.st_nlink
|
|
77
|
+
|
|
78
|
+
# Get UID and GID, convert to names
|
|
79
|
+
uid_name = pwd.getpwuid(file_stat.st_uid).pw_name
|
|
80
|
+
gid_name = grp.getgrgid(file_stat.st_gid).gr_name
|
|
81
|
+
|
|
82
|
+
# Get file size
|
|
83
|
+
size = file_stat.st_size
|
|
84
|
+
|
|
85
|
+
# Convert size to human-readable form
|
|
86
|
+
size_human = convert_size(size)
|
|
87
|
+
|
|
88
|
+
# Get last modification time
|
|
89
|
+
mtime = time.strftime("%Y-%m-%d %H:%M", time.localtime(file_stat.st_mtime))
|
|
90
|
+
|
|
91
|
+
# Append the information
|
|
92
|
+
files_info.append(
|
|
93
|
+
[
|
|
94
|
+
file_stat.st_ino,
|
|
95
|
+
permissions,
|
|
96
|
+
hard_links,
|
|
97
|
+
uid_name,
|
|
98
|
+
gid_name,
|
|
99
|
+
size_human,
|
|
100
|
+
mtime,
|
|
101
|
+
file_name,
|
|
102
|
+
]
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Print in tabular format
|
|
106
|
+
headers = [
|
|
107
|
+
"Inode",
|
|
108
|
+
"Permissions",
|
|
109
|
+
"Links",
|
|
110
|
+
"UID",
|
|
111
|
+
"GID",
|
|
112
|
+
"Size",
|
|
113
|
+
"Last Modified",
|
|
114
|
+
"Name",
|
|
115
|
+
]
|
|
116
|
+
print(tabulate(files_info, headers=headers, tablefmt="plain"))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def convert_size(size_bytes):
|
|
120
|
+
"""Convert a size in bytes to a human-readable string."""
|
|
121
|
+
if size_bytes == 0:
|
|
122
|
+
return "0B"
|
|
123
|
+
size_name = ("B", "K", "M", "G", "T", "P", "E", "Z", "Y")
|
|
124
|
+
i = int(math.floor(math.log(size_bytes, 1024)))
|
|
125
|
+
p = math.pow(1024, i)
|
|
126
|
+
s = round(size_bytes / p, 2)
|
|
127
|
+
return f"{s}{size_name[i]}"
|
|
128
|
+
|
|
129
|
+
|
|
52
130
|
@dataclass
|
|
53
131
|
class ReplTheme(Theme):
|
|
54
132
|
title: str = "bold cyan"
|
|
@@ -332,6 +410,76 @@ class ReplBase:
|
|
|
332
410
|
self.commands[cmd_name] = new_cmd
|
|
333
411
|
return new_cmd
|
|
334
412
|
|
|
413
|
+
def setup_cmds(self, *cmd_names: list[str]) -> None:
|
|
414
|
+
"""Automatically configure commands based on the provided names
|
|
415
|
+
The ReplCommand objects are populated based on function meta
|
|
416
|
+
data retrieved from the provided function names
|
|
417
|
+
Args:
|
|
418
|
+
cmd_names (list[str]): List of class methods to convert to REPL commands
|
|
419
|
+
"""
|
|
420
|
+
|
|
421
|
+
funcs: list[Callable] = [
|
|
422
|
+
getattr(self, name)
|
|
423
|
+
for name in cmd_names
|
|
424
|
+
if hasattr(self, name) and callable(getattr(self, name))
|
|
425
|
+
]
|
|
426
|
+
|
|
427
|
+
for func in funcs:
|
|
428
|
+
help_text = func.__doc__
|
|
429
|
+
name = func.__name__
|
|
430
|
+
self.add_command(name, func, help_txt=help_text)
|
|
431
|
+
|
|
432
|
+
def warn(self, msg: str) -> None:
|
|
433
|
+
"""Print a message to the REPL preformatted as a warning"""
|
|
434
|
+
|
|
435
|
+
self.print(f"[warn]{msg}[/warn]")
|
|
436
|
+
|
|
437
|
+
def pretty_print(self, obj: Any) -> None:
|
|
438
|
+
pretty.pprint(obj)
|
|
439
|
+
|
|
440
|
+
def pwd(self) -> str:
|
|
441
|
+
"""Print out the current path location
|
|
442
|
+
|
|
443
|
+
Returns:
|
|
444
|
+
str: The current path
|
|
445
|
+
"""
|
|
446
|
+
|
|
447
|
+
self.print(f"Current location: {os.getcwd()}")
|
|
448
|
+
return os.getcwd()
|
|
449
|
+
|
|
450
|
+
def cd(self, path: str = "..") -> str:
|
|
451
|
+
"""Move the terminal to a new path location
|
|
452
|
+
|
|
453
|
+
Args:
|
|
454
|
+
path (str, optional): Path to move to. Defaults to "..".
|
|
455
|
+
|
|
456
|
+
Returns:
|
|
457
|
+
str: The new location
|
|
458
|
+
"""
|
|
459
|
+
|
|
460
|
+
try:
|
|
461
|
+
os.chdir(path)
|
|
462
|
+
except FileNotFoundError:
|
|
463
|
+
self.warn(
|
|
464
|
+
f"Path: {path} does not exist, remaining in current directory"
|
|
465
|
+
)
|
|
466
|
+
new_pwd = os.getcwd()
|
|
467
|
+
self.print(f"New dir: {new_pwd}")
|
|
468
|
+
return new_pwd
|
|
469
|
+
|
|
470
|
+
def ls(self, path: str = ".") -> list[str]:
|
|
471
|
+
"""List metadata about the files/directories at the given path
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
path (str, optional): Path to list. Defaults to ".".
|
|
475
|
+
|
|
476
|
+
Returns:
|
|
477
|
+
list[str]: List of file and directory names at the location
|
|
478
|
+
"""
|
|
479
|
+
|
|
480
|
+
ls_liah(path)
|
|
481
|
+
return os.listdir(path)
|
|
482
|
+
|
|
335
483
|
def interactive(self, *args, **kwargs) -> None:
|
|
336
484
|
"""Starts an interactive session from within the class"""
|
|
337
485
|
if kwargs:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|