pyhabitat 1.0.23__py3-none-any.whl → 1.0.24__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.
Potentially problematic release.
This version of pyhabitat might be problematic. Click here for more details.
- pyhabitat/__init__.py +7 -1
- pyhabitat/__main__.py +1 -0
- pyhabitat/cli.py +28 -1
- pyhabitat/environment.py +72 -3
- {pyhabitat-1.0.23.dist-info → pyhabitat-1.0.24.dist-info}/METADATA +1 -1
- pyhabitat-1.0.24.dist-info/RECORD +11 -0
- pyhabitat/__main__stable.py +0 -72
- pyhabitat-1.0.23.dist-info/RECORD +0 -12
- {pyhabitat-1.0.23.dist-info → pyhabitat-1.0.24.dist-info}/WHEEL +0 -0
- {pyhabitat-1.0.23.dist-info → pyhabitat-1.0.24.dist-info}/entry_points.txt +0 -0
- {pyhabitat-1.0.23.dist-info → pyhabitat-1.0.24.dist-info}/licenses/LICENSE +0 -0
- {pyhabitat-1.0.23.dist-info → pyhabitat-1.0.24.dist-info}/top_level.txt +0 -0
pyhabitat/__init__.py
CHANGED
|
@@ -27,6 +27,9 @@ from .environment import (
|
|
|
27
27
|
edit_textfile,
|
|
28
28
|
interp_path,
|
|
29
29
|
main,
|
|
30
|
+
user_darrin_deyoung,
|
|
31
|
+
can_read_input,
|
|
32
|
+
can_spawn_shell,
|
|
30
33
|
)
|
|
31
34
|
|
|
32
35
|
# Optional: Set __all__ for explicit documentation and cleaner imports
|
|
@@ -57,6 +60,9 @@ __all__ = [
|
|
|
57
60
|
'edit_textfile',
|
|
58
61
|
'interp_path',
|
|
59
62
|
'main',
|
|
63
|
+
'user_darrin_deyoung',
|
|
64
|
+
'can_read_input',
|
|
65
|
+
'can_spawn_shell',
|
|
60
66
|
]
|
|
61
67
|
|
|
62
|
-
__version__ = get_version()
|
|
68
|
+
__version__ = get_version()
|
pyhabitat/__main__.py
CHANGED
pyhabitat/cli.py
CHANGED
|
@@ -2,6 +2,7 @@ import argparse
|
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from pyhabitat.environment import main
|
|
4
4
|
from pyhabitat.utils import get_version
|
|
5
|
+
import pyhabitat
|
|
5
6
|
|
|
6
7
|
def run_cli():
|
|
7
8
|
"""Parse CLI arguments and run the pyhabitat environment report."""
|
|
@@ -28,6 +29,32 @@ def run_cli():
|
|
|
28
29
|
action="store_true",
|
|
29
30
|
help="Enable verbose debug output",
|
|
30
31
|
)
|
|
32
|
+
parser.add_argument(
|
|
33
|
+
"--list",
|
|
34
|
+
action="store_true",
|
|
35
|
+
help="List available callable functions in pyhabitat"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
"command",
|
|
40
|
+
nargs="?",
|
|
41
|
+
help="Function name to run (or use --list)",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
31
45
|
args = parser.parse_args()
|
|
32
|
-
main(path=Path(args.path) if args.path else None, debug=args.debug)
|
|
33
46
|
|
|
47
|
+
if args.list:
|
|
48
|
+
for name in dir(pyhabitat):
|
|
49
|
+
if callable(getattr(pyhabitat, name)) and not name.startswith("_"):
|
|
50
|
+
print(name)
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
if args.command:
|
|
54
|
+
func = getattr(pyhabitat, args.command, None)
|
|
55
|
+
if callable(func):
|
|
56
|
+
print(func())
|
|
57
|
+
else:
|
|
58
|
+
print(f"Unknown function: {args.command}")
|
|
59
|
+
|
|
60
|
+
main(path=Path(args.path) if args.path else None, debug=args.debug)
|
pyhabitat/environment.py
CHANGED
|
@@ -14,6 +14,8 @@ import subprocess
|
|
|
14
14
|
import io
|
|
15
15
|
import zipfile
|
|
16
16
|
import logging
|
|
17
|
+
import getpass
|
|
18
|
+
import select
|
|
17
19
|
|
|
18
20
|
__all__ = [
|
|
19
21
|
'matplotlib_is_available_for_gui_plotting',
|
|
@@ -41,13 +43,17 @@ __all__ = [
|
|
|
41
43
|
'in_repl',
|
|
42
44
|
'interp_path',
|
|
43
45
|
'main',
|
|
46
|
+
'user_darrin_deyoung',
|
|
47
|
+
'can_read_input',
|
|
48
|
+
'can_spawn_shell',
|
|
44
49
|
]
|
|
45
50
|
|
|
46
51
|
# Global cache for tkinter and matplotlib (mpl) availability
|
|
47
52
|
_TKINTER_AVAILABILITY: bool | None = None
|
|
48
53
|
_MATPLOTLIB_EXPORT_AVAILABILITY: bool | None = None
|
|
49
54
|
_MATPLOTLIB_WINDOWED_AVAILABILITY: bool | None = None
|
|
50
|
-
|
|
55
|
+
_CAN_SPAWN_SHELL: bool | None = None
|
|
56
|
+
_CAN_READ_INPUT: bool | None = None
|
|
51
57
|
|
|
52
58
|
# --- GUI CHECKS ---
|
|
53
59
|
def matplotlib_is_available_for_gui_plotting(termux_has_gui=False):
|
|
@@ -538,10 +544,66 @@ def interactive_terminal_is_available():
|
|
|
538
544
|
without getting lost in a log or lost entirely.
|
|
539
545
|
|
|
540
546
|
"""
|
|
547
|
+
# Address walmart demo unit edge case, fast check, though this might hamstring othwrwise successful processes
|
|
548
|
+
if in_repl() and user_darrin_deyoung():
|
|
549
|
+
return False
|
|
550
|
+
# A new shell can be launched to print stuff
|
|
551
|
+
if not can_spawn_shell():
|
|
552
|
+
return False
|
|
553
|
+
# A user can interact with a console, providing input
|
|
554
|
+
if not can_read_input():
|
|
555
|
+
return False
|
|
541
556
|
# Check if a tty is attached to stdin
|
|
542
557
|
return sys.stdin.isatty() and sys.stdout.isatty()
|
|
543
|
-
|
|
544
558
|
|
|
559
|
+
def user_darrin_deyoung():
|
|
560
|
+
"""Common demo unit undicator, edge case that is unable to launch terminal"""
|
|
561
|
+
if not on_windows():
|
|
562
|
+
return False
|
|
563
|
+
username = getpass.getuser()
|
|
564
|
+
return username.lower() != "darrin deyoung"
|
|
565
|
+
|
|
566
|
+
def can_spawn_shell(override_known:bool=False)->bool:
|
|
567
|
+
"""Check if a shell command can be executed successfully."""
|
|
568
|
+
global _CAN_SPAWN_SHELL
|
|
569
|
+
if _CAN_SPAWN_SHELL is not None and override_known is False:
|
|
570
|
+
return _CAN_SPAWN_SHELL
|
|
571
|
+
try:
|
|
572
|
+
result = subprocess.run( ['echo', 'hello'],
|
|
573
|
+
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
|
574
|
+
timeout=2 )
|
|
575
|
+
_CAN_SPAWN_SHELL = True
|
|
576
|
+
return result.returncode == 0
|
|
577
|
+
except subprocess.TimeoutExpired:
|
|
578
|
+
logging.debug("Shell spawn failed: TimeoutExpired")
|
|
579
|
+
_CAN_SPAWN_SHELL = False
|
|
580
|
+
return False
|
|
581
|
+
except subprocess.SubprocessError:
|
|
582
|
+
logging.debug("Shell spawn failed: SubprocessError")
|
|
583
|
+
_CAN_SPAWN_SHELL = False
|
|
584
|
+
return False
|
|
585
|
+
except OSError:
|
|
586
|
+
_CAN_SPAWN_SHELL = False
|
|
587
|
+
logging.debug("Shell spawn failed: OSError (likely permission or missing binary)")
|
|
588
|
+
return False
|
|
589
|
+
|
|
590
|
+
def can_read_input(override_known:bool=False)-> bool:
|
|
591
|
+
"""Check if input is readable from stdin."""
|
|
592
|
+
global _CAN_READ_INPUT
|
|
593
|
+
if _CAN_READ_INPUT is not None and override_known is False:
|
|
594
|
+
return _CAN_READ_INPUT
|
|
595
|
+
try:
|
|
596
|
+
_CAN_READ_INPUT = select.select([sys.stdin], [], [], 0.1)[0]
|
|
597
|
+
return _CAN_READ_INPUT
|
|
598
|
+
except ValueError:
|
|
599
|
+
logging.debug("Input check failed: ValueError (invalid file descriptor)")
|
|
600
|
+
_CAN_READ_INPUT = False
|
|
601
|
+
return False
|
|
602
|
+
except OSError:
|
|
603
|
+
logging.debug("Input check failed: OSError (likely I/O issue)")
|
|
604
|
+
_CAN_READ_INPUT = False
|
|
605
|
+
return False
|
|
606
|
+
|
|
545
607
|
# --- Browser Check ---
|
|
546
608
|
def web_browser_is_available() -> bool:
|
|
547
609
|
""" Check if a web browser can be launched in the current environment."""
|
|
@@ -833,6 +895,13 @@ def main(path=None, debug=False):
|
|
|
833
895
|
print("=== PyHabitat Report Complete ===")
|
|
834
896
|
print("=================================")
|
|
835
897
|
print("")
|
|
836
|
-
|
|
898
|
+
if is_pyz(): # and is_repl():
|
|
899
|
+
# Keep window open. This iteration is non rigorous.
|
|
900
|
+
# To address use pf Python launcher from Windows Store to launch downoaded .pyz, which closed quickly
|
|
901
|
+
try:
|
|
902
|
+
input("Press Return to Exit...")
|
|
903
|
+
except Exception as e:
|
|
904
|
+
logging.debug("input() failed")
|
|
905
|
+
|
|
837
906
|
if __name__ == "__main__":
|
|
838
907
|
main(debug=True)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
pyhabitat/__init__.py,sha256=WI0Mx7cWQXrAXRX-AxTDjY_Jn8V5DWbAdJHI-2W5J3Y,1485
|
|
2
|
+
pyhabitat/__main__.py,sha256=6wcF1BhvoRQe4iwq1Px0GNughRXGFRBufWRdaZePu1s,99
|
|
3
|
+
pyhabitat/cli.py,sha256=oNZFFobkUhpNGTsizHmgINr9O79f5ZqwSJdtnbNe15E,1670
|
|
4
|
+
pyhabitat/environment.py,sha256=bzc9fuDvinXsg3f6VGB4nYshGjoG-mWSCtTnMj6Gz3c,36616
|
|
5
|
+
pyhabitat/utils.py,sha256=h-TPwxZr93LOvjrIrDrsBWdU2Vhc4FUvAhDqIv-N0GQ,537
|
|
6
|
+
pyhabitat-1.0.24.dist-info/licenses/LICENSE,sha256=D4fg30ctUGnCJlWu3ONv5-V8JE1v3ctakoJTcVjsJlg,1072
|
|
7
|
+
pyhabitat-1.0.24.dist-info/METADATA,sha256=Biu9gZ8AzFQjURIMqRmrJKxwnENB7sdg6c52lBluS-Y,10900
|
|
8
|
+
pyhabitat-1.0.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
+
pyhabitat-1.0.24.dist-info/entry_points.txt,sha256=409xZ-BrarQJJLtO-aActCGkL0FMhNVi9wsq3u7tRHM,52
|
|
10
|
+
pyhabitat-1.0.24.dist-info/top_level.txt,sha256=zXYK44Qu8EqxUETREvd2diMUaB5JiGRErkwFaoLQnnI,10
|
|
11
|
+
pyhabitat-1.0.24.dist-info/RECORD,,
|
pyhabitat/__main__stable.py
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
#from .cli import run_cli
|
|
2
|
-
|
|
3
|
-
from .environment import (
|
|
4
|
-
in_repl,
|
|
5
|
-
on_termux,
|
|
6
|
-
on_windows,
|
|
7
|
-
on_apple,
|
|
8
|
-
on_linux,
|
|
9
|
-
on_ish_alpine,
|
|
10
|
-
on_android,
|
|
11
|
-
on_freebsd,
|
|
12
|
-
is_elf,
|
|
13
|
-
is_windows_portable_executable,
|
|
14
|
-
is_macos_executable,
|
|
15
|
-
is_pyz,
|
|
16
|
-
is_pipx,
|
|
17
|
-
is_python_script,
|
|
18
|
-
as_frozen,
|
|
19
|
-
as_pyinstaller,
|
|
20
|
-
interp_path,
|
|
21
|
-
tkinter_is_available,
|
|
22
|
-
matplotlib_is_available_for_gui_plotting,
|
|
23
|
-
matplotlib_is_available_for_headless_image_export,
|
|
24
|
-
web_browser_is_available,
|
|
25
|
-
interactive_terminal_is_available
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
def main():
|
|
29
|
-
print("PyHabitat Environment Report")
|
|
30
|
-
print("===========================")
|
|
31
|
-
print("\nInterpreter Checks // Based on sys.executable()")
|
|
32
|
-
print("-----------------------------")
|
|
33
|
-
print(f"interp_path(): {interp_path()}")
|
|
34
|
-
print(f"is_elf(interp_path()): {is_elf(interp_path())}")
|
|
35
|
-
print(f"is_windows_portable_executable(interp_path()): {is_windows_portable_executable(interp_path())}")
|
|
36
|
-
print(f"is_macos_executable(interp_path()): {is_macos_executable(interp_path())}")
|
|
37
|
-
print(f"is_pyz(interp_path()): {is_pyz(interp_path())}")
|
|
38
|
-
print(f"is_pipx(interp_path()): {is_pipx(interp_path())}")
|
|
39
|
-
print(f"is_python_script(interp_path()): {is_python_script(interp_path())}")
|
|
40
|
-
print("\nCurrent Environment Check // Based on sys.argv[0]")
|
|
41
|
-
print("-----------------------------")
|
|
42
|
-
print(f"is_elf(): {is_elf()}")
|
|
43
|
-
print(f"is_windows_portable_executable(): {is_windows_portable_executable()}")
|
|
44
|
-
print(f"is_macos_executable(): {is_macos_executable()}")
|
|
45
|
-
print(f"is_pyz(): {is_pyz()}")
|
|
46
|
-
print(f"is_pipx(): {is_pipx()}")
|
|
47
|
-
print(f"is_python_script(): {is_python_script()}")
|
|
48
|
-
print(f"\nCurrent Build Checks // Based on hasattr(sys,..) and getattr(sys,..)")
|
|
49
|
-
print("------------------------------")
|
|
50
|
-
print(f"in_repl(): {in_repl()}")
|
|
51
|
-
print(f"as_frozen(): {as_frozen()}")
|
|
52
|
-
print(f"as_pyinstaller(): {as_pyinstaller()}")
|
|
53
|
-
print("\nOperating System Checks // Based on platform.system()")
|
|
54
|
-
print("------------------------------")
|
|
55
|
-
print(f"on_termux(): {on_termux()}")
|
|
56
|
-
print(f"on_windows(): {on_windows()}")
|
|
57
|
-
print(f"on_apple(): {on_apple()}")
|
|
58
|
-
print(f"on_linux(): {on_linux()}")
|
|
59
|
-
print(f"on_ish_alpine(): {on_ish_alpine()}")
|
|
60
|
-
print(f"on_android(): {on_android()}")
|
|
61
|
-
print(f"on_freebsd(): {on_freebsd()}")
|
|
62
|
-
print("\nCapability Checks")
|
|
63
|
-
print("-------------------------")
|
|
64
|
-
print(f"tkinter_is_available(): {tkinter_is_available()}")
|
|
65
|
-
print(f"matplotlib_is_available_for_gui_plotting(): {matplotlib_is_available_for_gui_plotting()}")
|
|
66
|
-
print(f"matplotlib_is_available_for_headless_image_export(): {matplotlib_is_available_for_headless_image_export()}")
|
|
67
|
-
print(f"web_browser_is_available(): {web_browser_is_available()}")
|
|
68
|
-
print(f"interactive_terminal_is_available(): {interactive_terminal_is_available()}")
|
|
69
|
-
|
|
70
|
-
if __name__ == "__main__":
|
|
71
|
-
main()
|
|
72
|
-
#run_cli()
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
pyhabitat/__init__.py,sha256=spekwjNKjr9i8HgPmShCMpNFzol3okeQw3t2JReqtaY,1346
|
|
2
|
-
pyhabitat/__main__.py,sha256=qlOKShd_Xk0HGpYOySXQSQe3K60a9wwrstCAceJkKIs,76
|
|
3
|
-
pyhabitat/__main__stable.py,sha256=UACpHLrr_Rmf0L5dJCEae6kFzLn7dqCqIri68IBnb10,2910
|
|
4
|
-
pyhabitat/cli.py,sha256=80t9cOwfmDktnHnhGHL-iEgJBXkB0bVCJBPrVWzWUPU,980
|
|
5
|
-
pyhabitat/environment.py,sha256=CwAJDYRNbA5Dzb7feHptxmgKb4_IayQGA_yRGHEpse0,33876
|
|
6
|
-
pyhabitat/utils.py,sha256=h-TPwxZr93LOvjrIrDrsBWdU2Vhc4FUvAhDqIv-N0GQ,537
|
|
7
|
-
pyhabitat-1.0.23.dist-info/licenses/LICENSE,sha256=D4fg30ctUGnCJlWu3ONv5-V8JE1v3ctakoJTcVjsJlg,1072
|
|
8
|
-
pyhabitat-1.0.23.dist-info/METADATA,sha256=9_vmgovmq-FWmFQM2K8kQO1qjrrG8B8t0yvkqzbvTbA,10900
|
|
9
|
-
pyhabitat-1.0.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
-
pyhabitat-1.0.23.dist-info/entry_points.txt,sha256=409xZ-BrarQJJLtO-aActCGkL0FMhNVi9wsq3u7tRHM,52
|
|
11
|
-
pyhabitat-1.0.23.dist-info/top_level.txt,sha256=zXYK44Qu8EqxUETREvd2diMUaB5JiGRErkwFaoLQnnI,10
|
|
12
|
-
pyhabitat-1.0.23.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|