pyhabitat 1.0.19__py3-none-any.whl → 1.0.21__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 +3 -1
- pyhabitat/cli.py +11 -0
- pyhabitat/environment.py +89 -81
- pyhabitat/utils.py +10 -0
- {pyhabitat-1.0.19.dist-info → pyhabitat-1.0.21.dist-info}/METADATA +1 -1
- pyhabitat-1.0.21.dist-info/RECORD +12 -0
- pyhabitat-1.0.19.dist-info/RECORD +0 -11
- {pyhabitat-1.0.19.dist-info → pyhabitat-1.0.21.dist-info}/WHEEL +0 -0
- {pyhabitat-1.0.19.dist-info → pyhabitat-1.0.21.dist-info}/entry_points.txt +0 -0
- {pyhabitat-1.0.19.dist-info → pyhabitat-1.0.21.dist-info}/licenses/LICENSE +0 -0
- {pyhabitat-1.0.19.dist-info → pyhabitat-1.0.21.dist-info}/top_level.txt +0 -0
pyhabitat/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# pyhabitat/__init__.py
|
|
2
|
-
|
|
2
|
+
from .utils import get_version
|
|
3
3
|
from .environment import (
|
|
4
4
|
matplotlib_is_available_for_gui_plotting,
|
|
5
5
|
matplotlib_is_available_for_headless_image_export,
|
|
@@ -58,3 +58,5 @@ __all__ = [
|
|
|
58
58
|
'interp_path',
|
|
59
59
|
'main',
|
|
60
60
|
]
|
|
61
|
+
|
|
62
|
+
__version__ = get_version()
|
pyhabitat/cli.py
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from pyhabitat.environment import main
|
|
4
|
+
from pyhabitat.utils import get_version
|
|
4
5
|
|
|
5
6
|
def run_cli():
|
|
6
7
|
"""Parse CLI arguments and run the pyhabitat environment report."""
|
|
8
|
+
current_version = get_version()
|
|
7
9
|
parser = argparse.ArgumentParser(
|
|
8
10
|
description="PyHabitat: Python environment and build introspection"
|
|
9
11
|
)
|
|
12
|
+
# Add the version argument
|
|
13
|
+
parser.add_argument(
|
|
14
|
+
'-v', '--version',
|
|
15
|
+
action='version',
|
|
16
|
+
version=f'%(prog)s {current_version}'
|
|
17
|
+
)
|
|
18
|
+
# Add the path argument
|
|
10
19
|
parser.add_argument(
|
|
11
20
|
"--path",
|
|
12
21
|
type=str,
|
|
13
22
|
default=None,
|
|
14
23
|
help="Path to a script or binary to inspect (defaults to sys.argv[0])",
|
|
15
24
|
)
|
|
25
|
+
# Add the debug argument
|
|
16
26
|
parser.add_argument(
|
|
17
27
|
"--debug",
|
|
18
28
|
action="store_true",
|
|
@@ -20,3 +30,4 @@ def run_cli():
|
|
|
20
30
|
)
|
|
21
31
|
args = parser.parse_args()
|
|
22
32
|
main(path=Path(args.path) if args.path else None, debug=args.debug)
|
|
33
|
+
|
pyhabitat/environment.py
CHANGED
|
@@ -75,7 +75,8 @@ def matplotlib_is_available_for_gui_plotting(termux_has_gui=False):
|
|
|
75
75
|
# Force the common GUI backend. At this point, we know tkinter is *available*.
|
|
76
76
|
# # 'TkAgg' is often the most reliable cross-platform test.
|
|
77
77
|
# 'TkAgg' != 'Agg'. The Agg backend is for non-gui image export.
|
|
78
|
-
matplotlib.
|
|
78
|
+
if matplotlib.get_backend().lower() != 'tkagg':
|
|
79
|
+
matplotlib.use('TkAgg', force=True)
|
|
79
80
|
import matplotlib.pyplot as plt
|
|
80
81
|
# A simple test call to ensure the backend initializes
|
|
81
82
|
# This final test catches any edge cases where tkinter is present but
|
|
@@ -226,18 +227,21 @@ def on_wsl():
|
|
|
226
227
|
if "WSL_DISTRO_NAME" in os.environ or "WSL_INTEROP" in os.environ:
|
|
227
228
|
return True
|
|
228
229
|
|
|
229
|
-
# --- Check kernel info for 'microsoft' string ---
|
|
230
|
+
# --- Check kernel info for 'microsoft' or 'wsl' string (Fallback) ---
|
|
230
231
|
# False negative risk:
|
|
231
232
|
# Custom kernels, future Windows versions, or minimal WSL distros may omit 'microsoft' in strings.
|
|
232
233
|
# False negative likelihood: Very low to moderate.
|
|
233
|
-
|
|
234
234
|
try:
|
|
235
235
|
with open("/proc/version") as f:
|
|
236
|
+
version_info = f.read().lower()
|
|
236
237
|
if "microsoft" in version_info or "wsl" in version_info:
|
|
237
238
|
return True
|
|
238
|
-
except
|
|
239
|
+
except (IOError, OSError):
|
|
240
|
+
# This block would catch the PermissionError!
|
|
241
|
+
# It would simply 'pass' and move on.
|
|
239
242
|
pass
|
|
240
|
-
|
|
243
|
+
|
|
244
|
+
|
|
241
245
|
# Check for WSL-specific mounts (fallback)
|
|
242
246
|
"""
|
|
243
247
|
/proc/sys/kernel/osrelease
|
|
@@ -245,17 +249,17 @@ def on_wsl():
|
|
|
245
249
|
Very reliable for detecting WSL1 and WSL2 unless someone compiled a custom kernel and removed the microsoft string.
|
|
246
250
|
|
|
247
251
|
False negative risk:
|
|
248
|
-
If /proc/
|
|
252
|
+
If /proc/sys/kernel/osrelease cannot be read due to permissions, a containerized WSL distro, or some sandboxed environment.
|
|
249
253
|
# False negative likelihood: Very low.
|
|
250
254
|
"""
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
255
|
+
try:
|
|
256
|
+
with open("/proc/sys/kernel/osrelease") as f:
|
|
257
|
+
osrelease = f.read().lower()
|
|
258
|
+
if "microsoft" in osrelease:
|
|
259
|
+
return True
|
|
260
|
+
except (IOError, OSError):
|
|
261
|
+
# This block would catch the PermissionError, an FileNotFound
|
|
262
|
+
pass
|
|
259
263
|
return False
|
|
260
264
|
|
|
261
265
|
def on_pydroid():
|
|
@@ -427,75 +431,52 @@ def is_macos_executable(exec_path: Path | str | None = None, debug: bool = False
|
|
|
427
431
|
def is_pipx(exec_path: Path | str | None = None, debug: bool = False, suppress_debug: bool = True) -> bool:
|
|
428
432
|
"""Checks if the executable is running from a pipx managed environment."""
|
|
429
433
|
exec_path, is_valid = _check_executable_path(exec_path, debug and not suppress_debug, check_pipx=False)
|
|
434
|
+
# check_pipx arg should be false when calling from inside of is_pipx() to avoid recursion error
|
|
435
|
+
# For safety, _check_executable_path() guards against this.
|
|
430
436
|
if not is_valid:
|
|
431
437
|
return False
|
|
432
438
|
|
|
433
439
|
try:
|
|
434
440
|
interpreter_path = Path(sys.executable).resolve()
|
|
435
441
|
pipx_bin_path, pipx_venv_base_path = _get_pipx_paths()
|
|
442
|
+
|
|
436
443
|
# Normalize paths for comparison
|
|
437
444
|
norm_exec_path = str(exec_path).lower()
|
|
438
445
|
norm_interp_path = str(interpreter_path).lower()
|
|
446
|
+
pipx_venv_base_str = str(pipx_venv_base_path).lower()
|
|
439
447
|
|
|
440
448
|
if debug:
|
|
441
449
|
logging.debug(f"EXEC_PATH: {exec_path}")
|
|
442
450
|
logging.debug(f"INTERP_PATH: {interpreter_path}")
|
|
443
451
|
logging.debug(f"PIPX_BIN_PATH: {pipx_bin_path}")
|
|
444
452
|
logging.debug(f"PIPX_VENV_BASE: {pipx_venv_base_path}")
|
|
445
|
-
is_in_pipx_venv_base = norm_interp_path.startswith(
|
|
453
|
+
is_in_pipx_venv_base = norm_interp_path.startswith(pipx_venv_base_str)
|
|
446
454
|
logging.debug(f"Interpreter path resides somewhere within the pipx venv base hierarchy: {is_in_pipx_venv_base}")
|
|
447
455
|
logging.debug(
|
|
448
456
|
f"This determines whether the current interpreter is managed by pipx: {is_in_pipx_venv_base}"
|
|
449
457
|
)
|
|
450
458
|
if "pipx/venvs" in norm_exec_path or "pipx/venvs" in norm_interp_path:
|
|
451
459
|
if debug:
|
|
452
|
-
logging.debug("True
|
|
460
|
+
logging.debug("is_pipx() is True // Signature Check")
|
|
453
461
|
return True
|
|
454
462
|
|
|
455
|
-
if norm_interp_path.startswith(
|
|
463
|
+
if norm_interp_path.startswith(pipx_venv_base_str):
|
|
456
464
|
if debug:
|
|
457
|
-
logging.debug("True
|
|
465
|
+
logging.debug("is_pipx() is True // Interpreter Base Check")
|
|
458
466
|
return True
|
|
459
467
|
|
|
460
|
-
if norm_exec_path.startswith(
|
|
468
|
+
if norm_exec_path.startswith(pipx_venv_base_str):
|
|
461
469
|
if debug:
|
|
462
|
-
logging.debug("True
|
|
470
|
+
logging.debug("is_pipx() is True // Executable Base Check")
|
|
463
471
|
return True
|
|
464
472
|
|
|
465
|
-
return False
|
|
466
|
-
except Exception:
|
|
467
473
|
if debug:
|
|
468
|
-
logging.debug("
|
|
474
|
+
logging.debug("is_pipx() is False")
|
|
469
475
|
return False
|
|
470
|
-
if debug:
|
|
471
|
-
logging.debug(f"EXEC_PATH: {exec_path}")
|
|
472
|
-
logging.debug(f"INTERP_PATH: {interpreter_path}")
|
|
473
|
-
logging.debug(f"PIPX_BIN_PATH: {pipx_bin_path}")
|
|
474
|
-
logging.debug(f"PIPX_VENV_BASE: {pipx_venv_base_path}")
|
|
475
|
-
logging.debug(f"Check B result: {norm_interp_path.startswith(str(pipx_venv_base_path).lower())}")
|
|
476
|
-
|
|
477
|
-
if "pipx/venvs" in norm_exec_path or "pipx/venvs" in norm_interp_path:
|
|
478
|
-
if debug:
|
|
479
|
-
logging.debug("True (Signature Check)")
|
|
480
|
-
return True
|
|
481
|
-
|
|
482
|
-
if norm_interp_path.startswith(str(pipx_venv_base_path).lower()):
|
|
483
|
-
if debug:
|
|
484
|
-
logging.debug("True (Interpreter Base Check)")
|
|
485
|
-
return True
|
|
486
|
-
|
|
487
|
-
if norm_exec_path.startswith(str(pipx_venv_base_path).lower()):
|
|
488
|
-
if debug:
|
|
489
|
-
logging.debug("True (Executable Base Check)")
|
|
490
|
-
return True
|
|
491
476
|
|
|
492
|
-
if debug:
|
|
493
|
-
logging.debug("False")
|
|
494
|
-
return False
|
|
495
477
|
except Exception:
|
|
496
478
|
if debug:
|
|
497
479
|
logging.debug("False (Exception during pipx check)")
|
|
498
|
-
return False
|
|
499
480
|
|
|
500
481
|
def is_python_script(path: Path | str | None = None, debug: bool = False, suppress_debug: bool =False) -> bool:
|
|
501
482
|
"""
|
|
@@ -574,8 +555,8 @@ def edit_textfile(path: Path | str | None = None) -> None:
|
|
|
574
555
|
#def open_text_file_for_editing(path): # defunct function name as of 1.0.16
|
|
575
556
|
"""
|
|
576
557
|
Opens a file with the environment's default application (Windows, Linux, macOS)
|
|
577
|
-
or a guaranteed console editor (nano) in constrained environments (Termux, iSH)
|
|
578
|
-
|
|
558
|
+
or a guaranteed console editor (nano) in constrained environments (Termux, iSH).
|
|
559
|
+
Ensures line-ending compatibility where possible.
|
|
579
560
|
|
|
580
561
|
This function is known to fail on PyDroid3, where on_linus() is True but xdg-open
|
|
581
562
|
is not available.
|
|
@@ -590,28 +571,28 @@ def edit_textfile(path: Path | str | None = None) -> None:
|
|
|
590
571
|
os.startfile(path)
|
|
591
572
|
elif on_termux():
|
|
592
573
|
# Install dependencies if missing (Termux pkg returns non-zero if already installed, so no check=True)
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
574
|
+
subprocess.run(['pkg','install', 'dos2unix', 'nano'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
575
|
+
_run_dos2unix(path)
|
|
576
|
+
subprocess.run(['nano', str(path)])
|
|
596
577
|
elif on_ish_alpine():
|
|
597
578
|
# Install dependencies if missing (apk returns 0 if already installed, so check=True is safe)
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
579
|
+
subprocess.run(['apk','add', 'dos2unix'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
|
|
580
|
+
subprocess.run(['apk','add', 'nano'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
|
|
581
|
+
_run_dos2unix(path)
|
|
582
|
+
subprocess.run(['nano', str(path)])
|
|
602
583
|
# --- Standard Unix-like Systems (Conversion + Default App) ---
|
|
603
584
|
elif on_linux():
|
|
604
|
-
|
|
605
|
-
|
|
585
|
+
_run_dos2unix(path) # Safety conversion for user-defined console apps
|
|
586
|
+
subprocess.run(['xdg-open', str(path)])
|
|
606
587
|
elif on_apple():
|
|
607
|
-
|
|
608
|
-
|
|
588
|
+
_run_dos2unix(path) # Safety conversion for user-defined console apps
|
|
589
|
+
subprocess.run(['open', str(path)])
|
|
609
590
|
else:
|
|
610
|
-
|
|
591
|
+
print("Unsupported operating system.")
|
|
611
592
|
except Exception as e:
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
593
|
+
print("The file could not be opened for editing in the current environment: {e}")
|
|
594
|
+
"""
|
|
595
|
+
Why Not Use check=True on Termux:
|
|
615
596
|
The pkg utility in Termux is a wrapper around Debian's apt. When you run pkg install <package>, if the package is already installed, the utility often returns an exit code of 100 (or another non-zero value) to indicate that no changes were made because the package was already present.
|
|
616
597
|
"""
|
|
617
598
|
|
|
@@ -633,8 +614,10 @@ def _run_dos2unix(path: Path | str | None = None):
|
|
|
633
614
|
# Catch other subprocess errors (e.g. permission issues)
|
|
634
615
|
pass
|
|
635
616
|
|
|
636
|
-
def read_magic_bytes(path: str, length: int = 4, debug: bool = False) -> bytes:
|
|
637
|
-
"""Return the first few bytes of a file for type detection.
|
|
617
|
+
def read_magic_bytes(path: str, length: int = 4, debug: bool = False) -> bytes | None:
|
|
618
|
+
"""Return the first few bytes of a file for type detection.
|
|
619
|
+
Returns None if the file cannot be read or does not exist.
|
|
620
|
+
"""
|
|
638
621
|
try:
|
|
639
622
|
with open(path, "rb") as f:
|
|
640
623
|
magic = f.read(length)
|
|
@@ -644,7 +627,9 @@ def read_magic_bytes(path: str, length: int = 4, debug: bool = False) -> bytes:
|
|
|
644
627
|
except Exception as e:
|
|
645
628
|
if debug:
|
|
646
629
|
logging.debug(f"False (Error during file check: {e})")
|
|
647
|
-
return False
|
|
630
|
+
#return False # not typesafe
|
|
631
|
+
#return b'' # could be misunderstood as what was found
|
|
632
|
+
return None # no way to conflate that this was a legitimate error
|
|
648
633
|
|
|
649
634
|
def _get_pipx_paths():
|
|
650
635
|
"""
|
|
@@ -685,8 +670,19 @@ def _check_if_zip(path: Path | str | None) -> bool:
|
|
|
685
670
|
# Handle cases where the path might be invalid, or other unexpected errors
|
|
686
671
|
return False
|
|
687
672
|
|
|
688
|
-
def _check_executable_path(exec_path: Path | str | None,
|
|
689
|
-
|
|
673
|
+
def _check_executable_path(exec_path: Path | str | None,
|
|
674
|
+
debug: bool = False,
|
|
675
|
+
check_pipx: bool = True
|
|
676
|
+
) -> tuple[Path | None, bool]: #compensate with __future__, may cause type checker issues
|
|
677
|
+
"""
|
|
678
|
+
Helper function to resolve an executable path and perform common checks.
|
|
679
|
+
|
|
680
|
+
Returns:
|
|
681
|
+
tuple[Path | None, bool]: (Resolved path, is_valid)
|
|
682
|
+
- Path: The resolved Path object, or None if invalid
|
|
683
|
+
- bool: Whether the path should be considered valid for subsequent checks
|
|
684
|
+
"""
|
|
685
|
+
# 1. Determine path
|
|
690
686
|
if exec_path is None:
|
|
691
687
|
exec_path = Path(sys.argv[0]).resolve() if sys.argv[0] and sys.argv[0] != '-c' else None
|
|
692
688
|
else:
|
|
@@ -695,22 +691,31 @@ def _check_executable_path(exec_path: Path | str | None, debug: bool = False, ch
|
|
|
695
691
|
if debug:
|
|
696
692
|
logging.debug(f"Checking executable path: {exec_path}")
|
|
697
693
|
|
|
694
|
+
# 2. Handle missing path
|
|
698
695
|
if exec_path is None:
|
|
699
696
|
if debug:
|
|
700
|
-
logging.debug("
|
|
697
|
+
logging.debug("_check_executable_path() returns (None, False) // exec_path is None")
|
|
701
698
|
return None, False
|
|
702
|
-
|
|
703
|
-
|
|
699
|
+
|
|
700
|
+
# 3. Ensure path actually exists and is a file
|
|
701
|
+
if not exec_path.is_file():
|
|
704
702
|
if debug:
|
|
705
|
-
logging.debug("
|
|
703
|
+
logging.debug("_check_executable_path() returns (exec_path, False) // exec_path is not a file")
|
|
706
704
|
return exec_path, False
|
|
707
705
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
706
|
+
# 4. Avoid recursive pipx check loops
|
|
707
|
+
# This guard ensures we don’t recursively call _check_executable_path()
|
|
708
|
+
# via is_pipx() -> _check_executable_path() -> is_pipx() -> ...
|
|
709
|
+
if check_pipx:
|
|
710
|
+
caller = sys._getframe(1).f_code.co_name
|
|
711
|
+
if caller != "is_pipx":
|
|
712
|
+
if is_pipx(exec_path, debug):
|
|
713
|
+
if debug:
|
|
714
|
+
logging.debug("_check_executable_path() returns (exec_path, False) // is_pipx(exec_path) is True")
|
|
715
|
+
return exec_path, False
|
|
712
716
|
|
|
713
|
-
return exec_path, True
|
|
717
|
+
return exec_path, True
|
|
718
|
+
|
|
714
719
|
|
|
715
720
|
# --- Main Function for report and CLI compatibility ---
|
|
716
721
|
|
|
@@ -795,7 +800,7 @@ def main(path=None, debug=False):
|
|
|
795
800
|
# Supress redundant prints explicity using suppress_debug=True,
|
|
796
801
|
# so that only unique information gets printed for each check,
|
|
797
802
|
# even when more than one use the same functions which include debugging logs.
|
|
798
|
-
|
|
803
|
+
#print(f"_check_executable_path(script_path, debug=True)")
|
|
799
804
|
_check_executable_path(script_path, debug=debug)
|
|
800
805
|
#print(f"read_magic_bites(script_path, debug=True)")
|
|
801
806
|
read_magic_bytes(script_path, debug=debug)
|
|
@@ -818,4 +823,7 @@ def main(path=None, debug=False):
|
|
|
818
823
|
print("=================================")
|
|
819
824
|
print("=== PyHabitat Report Complete ===")
|
|
820
825
|
print("=================================")
|
|
821
|
-
print("")
|
|
826
|
+
print("")
|
|
827
|
+
|
|
828
|
+
if __name__ == "__main__":
|
|
829
|
+
main(debug=True)
|
pyhabitat/utils.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
2
|
+
def get_version() -> str:
|
|
3
|
+
"""Retrieves the installed package version."""
|
|
4
|
+
try:
|
|
5
|
+
# The package name 'pyhabitat' must exactly match the name in your pyproject.toml
|
|
6
|
+
return version('pyhabitat')
|
|
7
|
+
except PackageNotFoundError:
|
|
8
|
+
# This occurs if the script is run directly from the source directory
|
|
9
|
+
# without being installed in editable mode, or if the package name is wrong.
|
|
10
|
+
return "Not Installed (Local Development or Incorrect Name)"
|
|
@@ -0,0 +1,12 @@
|
|
|
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=7S6dqdmT9cheT2nkjwmLHGt8Iks1Fayezq5P7OEy8NI,33468
|
|
6
|
+
pyhabitat/utils.py,sha256=h-TPwxZr93LOvjrIrDrsBWdU2Vhc4FUvAhDqIv-N0GQ,537
|
|
7
|
+
pyhabitat-1.0.21.dist-info/licenses/LICENSE,sha256=D4fg30ctUGnCJlWu3ONv5-V8JE1v3ctakoJTcVjsJlg,1072
|
|
8
|
+
pyhabitat-1.0.21.dist-info/METADATA,sha256=DYmPZjp8zQrDX0nHfjG7oXHPyyIGr_Zqivmvl1q-t8o,10900
|
|
9
|
+
pyhabitat-1.0.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
+
pyhabitat-1.0.21.dist-info/entry_points.txt,sha256=409xZ-BrarQJJLtO-aActCGkL0FMhNVi9wsq3u7tRHM,52
|
|
11
|
+
pyhabitat-1.0.21.dist-info/top_level.txt,sha256=zXYK44Qu8EqxUETREvd2diMUaB5JiGRErkwFaoLQnnI,10
|
|
12
|
+
pyhabitat-1.0.21.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
pyhabitat/__init__.py,sha256=B_yS2yBG2kjHkte_xissSG8aJ15af-soBo3Cy7caiEc,1288
|
|
2
|
-
pyhabitat/__main__.py,sha256=qlOKShd_Xk0HGpYOySXQSQe3K60a9wwrstCAceJkKIs,76
|
|
3
|
-
pyhabitat/__main__stable.py,sha256=UACpHLrr_Rmf0L5dJCEae6kFzLn7dqCqIri68IBnb10,2910
|
|
4
|
-
pyhabitat/cli.py,sha256=ZlY6v_IT7Mw-ekR3WPT8NLsqMQ_RXI-cckVq9oLT4AU,683
|
|
5
|
-
pyhabitat/environment.py,sha256=2wyjdWNRSv8VxaXkjoQz285ATAlhddrY_dor45mMnQ4,32871
|
|
6
|
-
pyhabitat-1.0.19.dist-info/licenses/LICENSE,sha256=D4fg30ctUGnCJlWu3ONv5-V8JE1v3ctakoJTcVjsJlg,1072
|
|
7
|
-
pyhabitat-1.0.19.dist-info/METADATA,sha256=N8o0nM2KOq7X4B-JNUMcePtqjinqsdqrqZ11Ct8qsRY,10900
|
|
8
|
-
pyhabitat-1.0.19.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
-
pyhabitat-1.0.19.dist-info/entry_points.txt,sha256=409xZ-BrarQJJLtO-aActCGkL0FMhNVi9wsq3u7tRHM,52
|
|
10
|
-
pyhabitat-1.0.19.dist-info/top_level.txt,sha256=zXYK44Qu8EqxUETREvd2diMUaB5JiGRErkwFaoLQnnI,10
|
|
11
|
-
pyhabitat-1.0.19.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|