pyhabitat 1.0.30__tar.gz → 1.0.32__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.

Potentially problematic release.


This version of pyhabitat might be problematic. Click here for more details.

Files changed (21) hide show
  1. {pyhabitat-1.0.30/pyhabitat.egg-info → pyhabitat-1.0.32}/PKG-INFO +6 -2
  2. {pyhabitat-1.0.30/pyhabitat-build → pyhabitat-1.0.32}/pyhabitat/__init__.py +4 -0
  3. {pyhabitat-1.0.30/pyhabitat-build → pyhabitat-1.0.32}/pyhabitat/environment.py +104 -15
  4. pyhabitat-1.0.32/pyhabitat/report.py +0 -0
  5. {pyhabitat-1.0.30 → pyhabitat-1.0.32/pyhabitat.egg-info}/PKG-INFO +6 -2
  6. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/pyhabitat.egg-info/SOURCES.txt +1 -0
  7. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/pyproject.toml +10 -5
  8. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/LICENSE +0 -0
  9. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/README.md +0 -0
  10. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/pyhabitat/cli.py +0 -0
  11. /pyhabitat-1.0.30/pyhabitat/report.py → /pyhabitat-1.0.32/pyhabitat/demo.py +0 -0
  12. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/pyhabitat/utils.py +0 -0
  13. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/pyhabitat-build/__main__.py +0 -0
  14. {pyhabitat-1.0.30 → pyhabitat-1.0.32/pyhabitat-build}/pyhabitat/__init__.py +0 -0
  15. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/pyhabitat-build/pyhabitat/cli.py +0 -0
  16. {pyhabitat-1.0.30 → pyhabitat-1.0.32/pyhabitat-build}/pyhabitat/environment.py +0 -0
  17. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/pyhabitat-build/pyhabitat/utils.py +0 -0
  18. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/pyhabitat.egg-info/dependency_links.txt +0 -0
  19. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/pyhabitat.egg-info/entry_points.txt +0 -0
  20. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/pyhabitat.egg-info/top_level.txt +0 -0
  21. {pyhabitat-1.0.30 → pyhabitat-1.0.32}/setup.cfg +0 -0
@@ -1,13 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyhabitat
3
- Version: 1.0.30
3
+ Version: 1.0.32
4
4
  Summary: A lightweight library for detecting system environment, GUI, and build properties.
5
5
  Author-email: George Clayton Bennett <george.bennett@memphistn.gov>
6
6
  License-Expression: MIT
7
7
  Keywords: environment,os-detection,gui,build-system
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Operating System :: OS Independent
10
- Classifier: Topic :: System :: Systems Administration
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Environment :: Console
12
+ Classifier: Topic :: Software Development :: Build Tools
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Classifier: Topic :: Utilities
11
15
  Requires-Python: >=3.7
12
16
  Description-Content-Type: text/markdown
13
17
  License-File: LICENSE
@@ -30,6 +30,8 @@ from .environment import (
30
30
  user_darrin_deyoung,
31
31
  can_read_input,
32
32
  can_spawn_shell,
33
+ is_ascii,
34
+ is_binary,
33
35
  )
34
36
 
35
37
  # Optional: Set __all__ for explicit documentation and cleaner imports
@@ -63,6 +65,8 @@ __all__ = [
63
65
  'user_darrin_deyoung',
64
66
  'can_read_input',
65
67
  'can_spawn_shell',
68
+ 'is_ascii',
69
+ 'is_binary',
66
70
  ]
67
71
 
68
72
  __version__ = get_version()
@@ -17,6 +17,12 @@ import logging
17
17
  import getpass
18
18
  import select
19
19
 
20
+ # On Windows, we need the msvcrt module for non-blocking I/O
21
+ try:
22
+ import msvcrt
23
+ except ImportError:
24
+ msvcrt = None
25
+
20
26
  __all__ = [
21
27
  'matplotlib_is_available_for_gui_plotting',
22
28
  'matplotlib_is_available_for_headless_image_export',
@@ -512,6 +518,19 @@ def is_python_script(path: Path | str | None = None, debug: bool = False, suppre
512
518
  return False
513
519
  return exec_path.suffix.lower() == '.py'
514
520
 
521
+ # --- File encoding check ---
522
+ def is_binary(path:str|Path|None=None)->bool:
523
+ """
524
+ Target file is encoded as binary.
525
+ """
526
+ pass
527
+
528
+ def is_ascii(path:str|Path|None=None)->bool:
529
+ """
530
+ Target file is encoded as ascii, plaintext.
531
+ """
532
+ pass
533
+
515
534
  # --- Interpreter Check ---
516
535
 
517
536
  def interp_path(debug: bool = False) -> str:
@@ -543,21 +562,46 @@ def interactive_terminal_is_available():
543
562
  then typer.prompt() or input() will work reliably,
544
563
  without getting lost in a log or lost entirely.
545
564
 
565
+ Solution correctly identifies that true interactivity requires:
566
+ (1) a TTY (potential) connection
567
+ (2) the ability to execute
568
+ (3) the ability to read I/O
569
+ (4) ignores known limitatons in restrictive environments
570
+
571
+ Jargon:
572
+ A TTY, short for Teletypewriter or TeleTYpe,
573
+ is a conceptual or physical device that serves
574
+ as the interface for a user to interact with
575
+ a computer system.
546
576
  """
547
577
  # Address walmart demo unit edge case, fast check, though this might hamstring othwrwise successful processes
548
- if in_repl() and user_darrin_deyoung():
578
+ if user_darrin_deyoung():
549
579
  return False
550
- # A new shell can be launched to print stuff
580
+
581
+ # Check if a tty is attached to stdin,
582
+ # quick failure here if not before testing spwaning and reading
583
+ if not (sys.stdin.isatty() and sys.stdout.isatty()):
584
+ return False
585
+
586
+ # Check of a new shell can be launched to print stuff
551
587
  if not can_spawn_shell():
552
588
  return False
589
+
553
590
  # A user can interact with a console, providing input
554
591
  #if not can_read_input():
555
592
  # return False
556
- # Check if a tty is attached to stdin
593
+
557
594
  return sys.stdin.isatty() and sys.stdout.isatty()
558
595
 
559
596
  def user_darrin_deyoung():
560
597
  """Common demo unit undicator, edge case that is unable to launch terminal"""
598
+ # Enable teating on non-Windows, non-demo systems
599
+ # where this function would otherwise return False.
600
+ # Linux: `export USER_DARRIN_DEYOUNG=True`
601
+ if os.getenv('USER_DARRIN_DEYOUNG','').lower() == "true":
602
+ print("env var USER_DARRIN_DEYOUNG is set to True.")
603
+ return True
604
+ # Darrin Deyoung is the typical username on demo-mode Windows systems
561
605
  if not on_windows():
562
606
  return False
563
607
  username = getpass.getuser()
@@ -568,25 +612,33 @@ def can_spawn_shell(override_known:bool=False)->bool:
568
612
  global _CAN_SPAWN_SHELL
569
613
  if _CAN_SPAWN_SHELL is not None and override_known is False:
570
614
  return _CAN_SPAWN_SHELL
571
- try:
572
- result = subprocess.run( ['echo', 'hello'],
573
- stdout=subprocess.PIPE, stderr=subprocess.PIPE,
574
- timeout=2 )
575
-
576
- _CAN_SPAWN_SHELL = True
615
+
616
+ try:
617
+ # Use a simple, universally applicable command with shell=True
618
+ # 'true' on Linux/macOS, or a basic command on Windows via cmd.exe
619
+ # A simple 'echo' or 'exit 0' would also work
620
+ result = subprocess.run(
621
+ 'exit 0', # A shell-internal command that succeeds on most shells
622
+ stdout=subprocess.PIPE,
623
+ stderr=subprocess.PIPE,
624
+ timeout=2,
625
+ shell=True # <--- ESSENTIAL for cross-platform reliability
626
+ )
577
627
 
578
- return result.returncode == 0
628
+ _CAN_SPAWN_SHELL = result.returncode == 0
629
+ return _CAN_SPAWN_SHELL
630
+
579
631
  except subprocess.TimeoutExpired:
580
- logging.debug("Shell spawn failed: TimeoutExpired")
632
+ print("Shell spawn failed: TimeoutExpired")
581
633
  _CAN_SPAWN_SHELL = result.returncode == 0
582
634
  return _CAN_SPAWN_SHELL
583
635
  except subprocess.SubprocessError:
584
- logging.debug("Shell spawn failed: SubprocessError")
636
+ print("Shell spawn failed: SubprocessError")
585
637
  _CAN_SPAWN_SHELL = False
586
638
  return False
587
639
  except OSError:
588
640
  _CAN_SPAWN_SHELL = False
589
- logging.debug("Shell spawn failed: OSError (likely permission or missing binary)")
641
+ print("Shell spawn failed: OSError (likely permission or missing binary)")
590
642
  return False
591
643
 
592
644
  def can_read_input(override_known:bool=False)-> bool:
@@ -594,10 +646,43 @@ def can_read_input(override_known:bool=False)-> bool:
594
646
  global _CAN_READ_INPUT
595
647
  if _CAN_READ_INPUT is not None and override_known is False:
596
648
  return _CAN_READ_INPUT
649
+
650
+ # --- 1. Windows Specific Check (msvcrt) ---
651
+ if msvcrt is not None and sys.stdin.isatty():
652
+ try:
653
+ # msvcrt.kbhit() checks if a keyboard hit is present
654
+ # We don't read the input yet, just check if it's there
655
+ _CAN_READ_INPUT = msvcrt.kbhit()
656
+ # If kbhit returns True, it means a key press is waiting.
657
+ # We assume if the terminal *is* a TTY, it *can* read input.
658
+ # We can't actually call input() without blocking, so we check TTY instead.
659
+ if _CAN_READ_INPUT:
660
+ return True
661
+
662
+ # Since we are checking if a *user can* interact, if we are in a TTY,
663
+ # we assume the capability exists, even if nothing is currently buffered.
664
+ # This prevents the false negative when no key is pressed.
665
+ _CAN_READ_INPUT = True
666
+ return True
667
+
668
+ except Exception as e:
669
+ # Catch errors in the kbhit check itself
670
+ logging.debug(f"msvcrt check failed: {e}")
671
+ pass # Fall through to the select check
672
+
673
+ # --- 2. POSIX/General Check (select) ---
674
+ # This block is reliable on Linux/macOS and other POSIX systems.
597
675
  try:
598
- # ERROR THIS IS NOT A BOOLEAN, IS RETURNS []
676
+ # _CAN_READ_INPUT is assigned the read-ready list ([] or [sys.stdin])
677
+ # The return value is then the boolean conversion of that list's truthiness.
678
+ # 1. select.select(...) returns a 3-element tuple.
679
+ # 2. [0] gets the read-ready list (rlist).
680
+ # 3. Wrapping the result in bool() converts the list's truth value:
681
+ # - [] becomes False
682
+ # - [sys.stdin] becomes True
599
683
  _CAN_READ_INPUT = select.select([sys.stdin], [], [], 0.1)[0]
600
- return _CAN_READ_INPUT
684
+ # Return the boolean value of the list: True if [sys.stdin], False if []
685
+ return bool(_CAN_READ_INPUT) # <--- Requied to convert list to boolean
601
686
  except ValueError:
602
687
  logging.debug("Input check failed: ValueError (invalid file descriptor)")
603
688
  _CAN_READ_INPUT = False
@@ -606,6 +691,9 @@ def can_read_input(override_known:bool=False)-> bool:
606
691
  logging.debug("Input check failed: OSError (likely I/O issue)")
607
692
  _CAN_READ_INPUT = False
608
693
  return False
694
+
695
+ # Final fallback: if nothing worked, assume False
696
+ return False
609
697
 
610
698
  # --- Browser Check ---
611
699
  def web_browser_is_available() -> bool:
@@ -623,6 +711,7 @@ def web_browser_is_available() -> bool:
623
711
  if shutil.which("xdg-open"):
624
712
  return True
625
713
  return False
714
+
626
715
 
627
716
  # --- LAUNCH MECHANISMS BASED ON ENVIRONMENT ---
628
717
  def edit_textfile(path: Path | str | None = None) -> None:
File without changes
@@ -1,13 +1,17 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyhabitat
3
- Version: 1.0.30
3
+ Version: 1.0.32
4
4
  Summary: A lightweight library for detecting system environment, GUI, and build properties.
5
5
  Author-email: George Clayton Bennett <george.bennett@memphistn.gov>
6
6
  License-Expression: MIT
7
7
  Keywords: environment,os-detection,gui,build-system
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Operating System :: OS Independent
10
- Classifier: Topic :: System :: Systems Administration
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Environment :: Console
12
+ Classifier: Topic :: Software Development :: Build Tools
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Classifier: Topic :: Utilities
11
15
  Requires-Python: >=3.7
12
16
  Description-Content-Type: text/markdown
13
17
  License-File: LICENSE
@@ -3,6 +3,7 @@ README.md
3
3
  pyproject.toml
4
4
  pyhabitat/__init__.py
5
5
  pyhabitat/cli.py
6
+ pyhabitat/demo.py
6
7
  pyhabitat/environment.py
7
8
  pyhabitat/report.py
8
9
  pyhabitat/utils.py
@@ -6,21 +6,26 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "pyhabitat"
9
- version = "1.0.30"
10
- #dynamic = ["version"] #
9
+ version = "1.0.32"
10
+ #dynamic = ["version"]
11
11
  authors = [
12
12
  { name="George Clayton Bennett", email="george.bennett@memphistn.gov" },
13
13
  ]
14
14
  description = "A lightweight library for detecting system environment, GUI, and build properties."
15
15
  readme = "README.md"
16
16
  requires-python = ">=3.7"
17
- license = "MIT"
17
+ license = "MIT"
18
18
  keywords = ["environment", "os-detection", "gui", "build-system"]
19
- classifiers = [
19
+ classifiers=[
20
20
  "Programming Language :: Python :: 3",
21
21
  "Operating System :: OS Independent",
22
- "Topic :: System :: Systems Administration",
22
+ "Intended Audience :: Developers",
23
+ "Environment :: Console",
24
+ "Topic :: Software Development :: Build Tools",
25
+ "Topic :: Software Development :: Libraries :: Python Modules",
26
+ "Topic :: Utilities",
23
27
  ]
28
+
24
29
  dependencies = []
25
30
 
26
31
  [project.scripts]
File without changes
File without changes
File without changes
File without changes