pyhabitat 1.0.18__py3-none-any.whl → 1.0.20__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 CHANGED
@@ -5,12 +5,14 @@ from .environment import (
5
5
  matplotlib_is_available_for_headless_image_export,
6
6
  tkinter_is_available,
7
7
  in_repl,
8
- on_termux,
9
8
  on_freebsd,
10
9
  on_linux,
11
10
  on_android,
12
11
  on_windows,
12
+ on_wsl,
13
13
  on_apple,
14
+ on_termux,
15
+ on_pydroid,
14
16
  on_ish_alpine,
15
17
  as_pyinstaller,
16
18
  as_frozen,
@@ -34,6 +36,8 @@ __all__ = [
34
36
  'tkinter_is_available',
35
37
  'in_repl',
36
38
  'on_termux',
39
+ 'on_pydroid',
40
+ 'on_wsl',
37
41
  'on_freebsd',
38
42
  'on_linux',
39
43
  'on_android',
pyhabitat/__main__.py CHANGED
@@ -1,4 +1,4 @@
1
- from .cli import run_cli
1
+ from pyhabitat.cli import run_cli
2
2
 
3
3
  if __name__ == "__main__":
4
4
  run_cli()
pyhabitat/cli.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import argparse
2
2
  from pathlib import Path
3
- from . import main
3
+ from pyhabitat.environment import main
4
4
 
5
5
  def run_cli():
6
6
  """Parse CLI arguments and run the pyhabitat environment report."""
pyhabitat/environment.py CHANGED
@@ -22,8 +22,10 @@ __all__ = [
22
22
  'on_termux',
23
23
  'on_freebsd',
24
24
  'on_linux',
25
+ 'on_pydroid',
25
26
  'on_android',
26
27
  'on_windows',
28
+ 'on_wsl',
27
29
  'on_apple',
28
30
  'on_ish_alpine',
29
31
  'as_pyinstaller',
@@ -209,6 +211,67 @@ def on_android() -> bool:
209
211
  return False
210
212
  return "android" in platform.platform().lower()
211
213
 
214
+
215
+ def on_wsl():
216
+ """Return True if running inside Windows Subsystem for Linux (WSL or WSL2)."""
217
+ # Must look like Linux, not Windows
218
+ if platform.system() != "Linux":
219
+ return False
220
+
221
+
222
+ # --- Check environment variables for WSL2 ---
223
+ # False negative risk:
224
+ # Environment variables may be absent in older WSL1 installs.
225
+ # False negative likelihood: low.
226
+ if "WSL_DISTRO_NAME" in os.environ or "WSL_INTEROP" in os.environ:
227
+ return True
228
+
229
+ # --- Check kernel info for 'microsoft' or 'wsl' string (Fallback) ---
230
+ # False negative risk:
231
+ # Custom kernels, future Windows versions, or minimal WSL distros may omit 'microsoft' in strings.
232
+ # False negative likelihood: Very low to moderate.
233
+ try:
234
+ with open("/proc/version") as f:
235
+ version_info = f.read().lower()
236
+ if "microsoft" in version_info or "wsl" in version_info:
237
+ return True
238
+ except (IOError, OSError):
239
+ # This block would catch the PermissionError!
240
+ # It would simply 'pass' and move on.
241
+ pass
242
+
243
+
244
+ # Check for WSL-specific mounts (fallback)
245
+ """
246
+ /proc/sys/kernel/osrelease
247
+ Purpose: Contains the kernel release string. In WSL, it usually contains "microsoft" (WSL2) or "microsoft-standard" (WSL1).
248
+ Very reliable for detecting WSL1 and WSL2 unless someone compiled a custom kernel and removed the microsoft string.
249
+
250
+ False negative risk:
251
+ If /proc/sys/kernel/osrelease cannot be read due to permissions, a containerized WSL distro, or some sandboxed environment.
252
+ # False negative likelihood: Very low.
253
+ """
254
+ try:
255
+ with open("/proc/sys/kernel/osrelease") as f:
256
+ osrelease = f.read().lower()
257
+ if "microsoft" in osrelease:
258
+ return True
259
+ except (IOError, OSError):
260
+ # This block would catch the PermissionError, an FileNotFound
261
+ pass
262
+ return False
263
+
264
+ def on_pydroid():
265
+ """Return True if running under Pydroid 3 (Android app)."""
266
+ if not on_android():
267
+ return False
268
+
269
+ exe = (sys.executable or "").lower()
270
+ if "pydroid" in exe or "ru.iiec.pydroid3" in exe:
271
+ return True
272
+
273
+ return any("pydroid" in p.lower() for p in sys.path)
274
+
212
275
  def on_windows() -> bool:
213
276
  """Detect if running on Windows."""
214
277
  return platform.system() == 'Windows'
@@ -277,31 +340,29 @@ def as_frozen():
277
340
  return getattr(sys, 'frozen', False)
278
341
 
279
342
  # --- Binary Characteristic Checks ---
280
- def is_elf(exec_path: Path | str | None = None, debug: bool = False) -> bool:
343
+ def is_elf(exec_path: Path | str | None = None, debug: bool = False, suppress_debug: bool =False) -> bool:
281
344
  """Checks if the currently running executable (sys.argv[0]) is a standalone PyInstaller-built ELF binary."""
282
345
  # If it's a pipx installation, it is not the monolithic binary we are concerned with here.
283
- exec_path, is_valid = _check_executable_path(exec_path, debug)
346
+ exec_path, is_valid = _check_executable_path(exec_path, debug and not suppress_debug)
284
347
  if not is_valid:
285
348
  return False
286
349
 
287
350
  try:
288
351
  # Check the magic number: The first four bytes of an ELF file are 0x7f, 'E', 'L', 'F' (b'\x7fELF').
289
352
  # This is the most reliable way to determine if the executable is a native binary wrapper (like PyInstaller's).
290
- with open(exec_path, 'rb') as f:
291
- magic_bytes = f.read(4)
292
- if debug:
293
- logging.debug(f"Magic bytes: {magic_bytes}")
353
+ magic_bytes = read_magic_bytes(exec_path, 4, debug and not suppress_debug)
354
+
294
355
  return magic_bytes == b'\x7fELF'
295
356
  except Exception:
296
357
  if debug:
297
358
  logging.debug("False (Exception during file check)")
298
359
  return False
299
360
 
300
- def is_pyz(exec_path: Path | str | None = None, debug: bool = False) -> bool:
361
+ def is_pyz(exec_path: Path | str | None = None, debug: bool = False, suppress_debug: bool =False) -> bool:
301
362
  """Checks if the currently running executable (sys.argv[0]) is a PYZ zipapp ."""
302
363
 
303
364
  # If it's a pipx installation, it is not the monolithic binary we are concerned with here.
304
- exec_path, is_valid = _check_executable_path(exec_path, debug)
365
+ exec_path, is_valid = _check_executable_path(exec_path, debug and not suppress_debug)
305
366
  if not is_valid:
306
367
  return False
307
368
 
@@ -319,40 +380,34 @@ def is_pyz(exec_path: Path | str | None = None, debug: bool = False) -> bool:
319
380
  return True
320
381
 
321
382
 
322
- def is_windows_portable_executable(exec_path: Path | str | None = None, debug: bool = False) -> bool:
383
+ def is_windows_portable_executable(exec_path: Path | str | None = None, debug: bool = False, suppress_debug: bool =False) -> bool:
323
384
  """
324
385
  Checks if the specified path or sys.argv[0] is a Windows Portable Executable (PE) binary.
325
386
  Windows Portable Executables include .exe, .dll, and other binaries.
326
387
  The standard way to check for a PE is to look for the MZ magic number at the very beginning of the file.
327
388
  """
328
- exec_path, is_valid = _check_executable_path(exec_path, debug)
389
+ exec_path, is_valid = _check_executable_path(exec_path, debug and not suppress_debug)
329
390
  if not is_valid:
330
391
  return False
331
- try:
332
- with open(exec_path, 'rb') as f:
333
- magic_bytes = f.read(2)
334
- if debug:
335
- logging.debug(f"Magic bytes: {magic_bytes}")
336
- return magic_bytes == b'MZ'
337
- except Exception as e:
338
- if debug:
339
- logging.debug(f"False (Error during file check: {e})")
340
- return False
341
-
342
- def is_macos_executable(exec_path: Path | str | None = None, debug: bool = False) -> bool:
392
+ magic_bytes = read_magic_bytes(exec_path, 2, debug and not suppress_debug)
393
+ result = magic_bytes.startswith(b"MZ")
394
+
395
+ return result
396
+
397
+ def is_macos_executable(exec_path: Path | str | None = None, debug: bool = False, suppress_debug: bool =False) -> bool:
343
398
  """
344
399
  Checks if the currently running executable is a macOS/Darwin Mach-O binary,
345
400
  and explicitly excludes pipx-managed environments.
346
401
  """
347
- exec_path, is_valid = _check_executable_path(exec_path, debug)
402
+ exec_path, is_valid = _check_executable_path(exec_path, debug and not suppress_debug)
348
403
  if not is_valid:
349
404
  return False
350
405
 
351
406
  try:
352
407
  # Check the magic number: Mach-O binaries start with specific 4-byte headers.
353
408
  # Common ones are: b'\xfe\xed\xfa\xce' (32-bit) or b'\xfe\xed\xfa\xcf' (64-bit)
354
- with open(exec_path, 'rb') as f:
355
- magic_bytes = f.read(4)
409
+
410
+ magic_bytes = read_magic_bytes(exec_path, 4, debug and not suppress_debug)
356
411
 
357
412
  # Common Mach-O magic numbers (including their reversed-byte counterparts)
358
413
  MACHO_MAGIC = {
@@ -364,8 +419,6 @@ def is_macos_executable(exec_path: Path | str | None = None, debug: bool = False
364
419
 
365
420
  is_macho = magic_bytes in MACHO_MAGIC
366
421
 
367
- if debug:
368
- logging.debug(f"Magic bytes: {magic_bytes}")
369
422
 
370
423
  return is_macho
371
424
 
@@ -374,9 +427,9 @@ def is_macos_executable(exec_path: Path | str | None = None, debug: bool = False
374
427
  logging.debug("False (Exception during file check)")
375
428
  return False
376
429
 
377
- def is_pipx(exec_path: Path | str | None = None, debug: bool = False) -> bool:
430
+ def is_pipx(exec_path: Path | str | None = None, debug: bool = False, suppress_debug: bool = True) -> bool:
378
431
  """Checks if the executable is running from a pipx managed environment."""
379
- exec_path, is_valid = _check_executable_path(exec_path, debug, check_pipx=False)
432
+ exec_path, is_valid = _check_executable_path(exec_path, debug and not suppress_debug, check_pipx=False)
380
433
  if not is_valid:
381
434
  return False
382
435
 
@@ -392,8 +445,11 @@ def is_pipx(exec_path: Path | str | None = None, debug: bool = False) -> bool:
392
445
  logging.debug(f"INTERP_PATH: {interpreter_path}")
393
446
  logging.debug(f"PIPX_BIN_PATH: {pipx_bin_path}")
394
447
  logging.debug(f"PIPX_VENV_BASE: {pipx_venv_base_path}")
395
- logging.debug(f"Check B result: {norm_interp_path.startswith(str(pipx_venv_base_path).lower())}")
396
-
448
+ is_in_pipx_venv_base = norm_interp_path.startswith(str(pipx_venv_base_path).lower())
449
+ logging.debug(f"Interpreter path resides somewhere within the pipx venv base hierarchy: {is_in_pipx_venv_base}")
450
+ logging.debug(
451
+ f"This determines whether the current interpreter is managed by pipx: {is_in_pipx_venv_base}"
452
+ )
397
453
  if "pipx/venvs" in norm_exec_path or "pipx/venvs" in norm_interp_path:
398
454
  if debug:
399
455
  logging.debug("True (Signature Check)")
@@ -409,8 +465,6 @@ def is_pipx(exec_path: Path | str | None = None, debug: bool = False) -> bool:
409
465
  logging.debug("True (Executable Base Check)")
410
466
  return True
411
467
 
412
- if debug:
413
- logging.debug("False")
414
468
  return False
415
469
  except Exception:
416
470
  if debug:
@@ -445,7 +499,8 @@ def is_pipx(exec_path: Path | str | None = None, debug: bool = False) -> bool:
445
499
  if debug:
446
500
  logging.debug("False (Exception during pipx check)")
447
501
  return False
448
- def is_python_script(path: Path | str | None = None, debug: bool = False) -> bool:
502
+
503
+ def is_python_script(path: Path | str | None = None, debug: bool = False, suppress_debug: bool =False) -> bool:
449
504
  """
450
505
  Checks if the specified path or running script is a Python source file (.py).
451
506
 
@@ -459,14 +514,14 @@ def is_python_script(path: Path | str | None = None, debug: bool = False) -> boo
459
514
  Returns:
460
515
  bool: True if the specified or default path is a Python source file (.py); False otherwise.
461
516
  """
462
- exec_path, is_valid = _check_executable_path(path, debug, check_pipx=False)
517
+ exec_path, is_valid = _check_executable_path(path, debug and not suppress_debug, check_pipx=False)
463
518
  if not is_valid:
464
519
  return False
465
520
  return exec_path.suffix.lower() == '.py'
466
521
 
467
522
  # --- Interpreter Check ---
468
523
 
469
- def interp_path(print_path: bool = False) -> str:
524
+ def interp_path(debug: bool = False) -> str:
470
525
  """
471
526
  Returns the path to the Python interpreter binary and optionally prints it.
472
527
 
@@ -482,8 +537,8 @@ def interp_path(print_path: bool = False) -> str:
482
537
  str: The path to the Python interpreter binary, or an empty string if unavailable.
483
538
  """
484
539
  path = sys.executable
485
- if print_path:
486
- print(f"Python interpreter path: {path}")
540
+ if debug:
541
+ logging.debug(f"Python interpreter path: {path}")
487
542
  return path
488
543
 
489
544
  # --- TTY Check ---
@@ -580,7 +635,20 @@ def _run_dos2unix(path: Path | str | None = None):
580
635
  except Exception:
581
636
  # Catch other subprocess errors (e.g. permission issues)
582
637
  pass
583
-
638
+
639
+ def read_magic_bytes(path: str, length: int = 4, debug: bool = False) -> bytes:
640
+ """Return the first few bytes of a file for type detection."""
641
+ try:
642
+ with open(path, "rb") as f:
643
+ magic = f.read(length)
644
+ if debug:
645
+ logging.debug(f"Magic bytes: {magic!r}")
646
+ return magic
647
+ except Exception as e:
648
+ if debug:
649
+ logging.debug(f"False (Error during file check: {e})")
650
+ return False
651
+
584
652
  def _get_pipx_paths():
585
653
  """
586
654
  Returns the configured/default pipx binary and home directories.
@@ -659,21 +727,26 @@ def main(path=None, debug=False):
659
727
  if debug:
660
728
  logging.basicConfig(level=logging.DEBUG)
661
729
  logging.getLogger('matplotlib').setLevel(logging.WARNING) # Suppress matplotlib debug logs
662
- print("PyHabitat Environment Report")
663
- print("===========================")
664
- print("\nCurrent Build Checks // Based on hasattr(sys,..) and getattr(sys,..)")
730
+ print("================================")
731
+ print("======= PyHabitat Report =======")
732
+ print("================================")
733
+ print("\nCurrent Build Checks ")
734
+ print("# // Based on hasattr(sys,..) and getattr(sys,..)")
665
735
  print("------------------------------")
666
736
  print(f"in_repl(): {in_repl()}")
667
737
  print(f"as_frozen(): {as_frozen()}")
668
738
  print(f"as_pyinstaller(): {as_pyinstaller()}")
669
- print("\nOperating System Checks // Based on platform.system()")
739
+ print("\nOperating System Checks")
740
+ print("# // Based on platform.system()")
670
741
  print("------------------------------")
671
- print(f"on_termux(): {on_termux()}")
672
742
  print(f"on_windows(): {on_windows()}")
673
743
  print(f"on_apple(): {on_apple()}")
674
744
  print(f"on_linux(): {on_linux()}")
675
- print(f"on_ish_alpine(): {on_ish_alpine()}")
745
+ print(f"on_wsl(): {on_wsl()}")
676
746
  print(f"on_android(): {on_android()}")
747
+ print(f"on_termux(): {on_termux()}")
748
+ print(f"on_pydroid(): {on_pydroid()}")
749
+ print(f"on_ish_alpine(): {on_ish_alpine()}")
677
750
  print(f"on_freebsd(): {on_freebsd()}")
678
751
  print("\nCapability Checks")
679
752
  print("-------------------------")
@@ -682,16 +755,27 @@ def main(path=None, debug=False):
682
755
  print(f"matplotlib_is_available_for_headless_image_export(): {matplotlib_is_available_for_headless_image_export()}")
683
756
  print(f"web_browser_is_available(): {web_browser_is_available()}")
684
757
  print(f"interactive_terminal_is_available(): {interactive_terminal_is_available()}")
685
- print("\nInterpreter Checks // Based on sys.executable()")
758
+ print("\nInterpreter Checks")
759
+ print("# // Based on sys.executable()")
686
760
  print("-----------------------------")
687
761
  print(f"interp_path(): {interp_path()}")
688
- print(f"is_elf(interp_path()): {is_elf(interp_path(), debug=debug)}")
689
- print(f"is_windows_portable_executable(interp_path()): {is_windows_portable_executable(interp_path(), debug=debug)}")
690
- print(f"is_macos_executable(interp_path()): {is_macos_executable(interp_path(), debug=debug)}")
691
- print(f"is_pyz(interp_path()): {is_pyz(interp_path(), debug=debug)}")
692
- print(f"is_pipx(interp_path()): {is_pipx(interp_path(), debug=debug)}")
693
- print(f"is_python_script(interp_path()): {is_python_script(interp_path(), debug=debug)}")
694
- print("\nCurrent Environment Check // Based on sys.argv[0]")
762
+ if debug:
763
+ # Do these debug prints once to avoid redundant prints
764
+ # Supress redundant prints explicity using suppress_debug=True,
765
+ # so that only unique information gets printed for each check,
766
+ # even when more than one use the same functions which include debugging logs.
767
+ #print(f"_check_executable_path(interp_path(), debug=True)")
768
+ _check_executable_path(interp_path(), debug=debug)
769
+ #print(f"read_magic_bites(interp_path(), debug=True)")
770
+ read_magic_bytes(interp_path(), debug=debug)
771
+ print(f"is_elf(interp_path()): {is_elf(interp_path(), debug=debug, suppress_debug=True)}")
772
+ print(f"is_windows_portable_executable(interp_path()): {is_windows_portable_executable(interp_path(), debug=debug, suppress_debug=True)}")
773
+ print(f"is_macos_executable(interp_path()): {is_macos_executable(interp_path(), debug=debug, suppress_debug=True)}")
774
+ print(f"is_pyz(interp_path()): {is_pyz(interp_path(), debug=debug, suppress_debug=True)}")
775
+ print(f"is_pipx(interp_path()): {is_pipx(interp_path(), debug=debug, suppress_debug=True)}")
776
+ print(f"is_python_script(interp_path()): {is_python_script(interp_path(), debug=debug, suppress_debug=True)}")
777
+ print("\nCurrent Environment Check")
778
+ print("# // Based on sys.argv[0]")
695
779
  print("-----------------------------")
696
780
  inspect_path = path if path is not None else (None if sys.argv[0] == '-c' else sys.argv[0])
697
781
  logging.debug(f"Inspecting path: {inspect_path}")
@@ -706,13 +790,35 @@ def main(path=None, debug=False):
706
790
  script_path = None
707
791
  if path or (sys.argv[0] and sys.argv[0] != '-c'):
708
792
  script_path = Path(path or sys.argv[0]).resolve()
709
- logging.debug(f"Script path resolved: {script_path}")
793
+ print(f"sys.argv[0] = {str(sys.argv[0])}")
710
794
  if script_path is not None:
711
- print(f"is_elf(): {is_elf(script_path, debug=debug)}")
712
- print(f"is_windows_portable_executable(): {is_windows_portable_executable(script_path, debug=debug)}")
713
- print(f"is_macos_executable(): {is_macos_executable(script_path, debug=debug)}")
714
- print(f"is_pyz(): {is_pyz(script_path, debug=debug)}")
715
- print(f"is_pipx(): {is_pipx(script_path, debug=debug)}")
716
- print(f"is_python_script(): {is_python_script(script_path, debug=debug)}")
795
+ print(f"script_path = {script_path}")
796
+ if debug:
797
+ # Do these debug prints once to avoid redundant prints
798
+ # Supress redundant prints explicity using suppress_debug=True,
799
+ # so that only unique information gets printed for each check,
800
+ # even when more than one use the same functions which include debugging logs.
801
+ p#rint(f"_check_executable_path(script_path, debug=True)")
802
+ _check_executable_path(script_path, debug=debug)
803
+ #print(f"read_magic_bites(script_path, debug=True)")
804
+ read_magic_bytes(script_path, debug=debug)
805
+ print(f"is_elf(): {is_elf(script_path, debug=debug, suppress_debug=True)}")
806
+ print(f"is_windows_portable_executable(): {is_windows_portable_executable(script_path, debug=debug, suppress_debug=True)}")
807
+ print(f"is_macos_executable(): {is_macos_executable(script_path, debug=debug, suppress_debug=True)}")
808
+ print(f"is_pyz(): {is_pyz(script_path, debug=debug, suppress_debug=True)}")
809
+ print(f"is_pipx(): {is_pipx(script_path, debug=debug, suppress_debug=True)}")
810
+ print(f"is_python_script(): {is_python_script(script_path, debug=debug, suppress_debug=True)}")
717
811
  else:
718
- print("script_path is None")
812
+ print("Skipping: ")
813
+ print(" is_elf(), ")
814
+ print(" is_windows_portable_executable(), ")
815
+ print(" is_macos_executable(), ")
816
+ print(" is_pyz(), ")
817
+ print(" is_pipx(), ")
818
+ print(" is_python_script(), ")
819
+ print("All False, script_path is None.")
820
+ print("")
821
+ print("=================================")
822
+ print("=== PyHabitat Report Complete ===")
823
+ print("=================================")
824
+ print("")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyhabitat
3
- Version: 1.0.18
3
+ Version: 1.0.20
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
@@ -78,10 +78,12 @@ Key question: "What is this running on?"
78
78
  | `on_windows()` | Returns `True` on Windows. |
79
79
  | `on_apple()` | Returns `True` on macOS (Darwin). |
80
80
  | `on_linux()` | Returns `True` on Linux in general. |
81
+ | `on_wsl()` | Returns `True` if running inside Windows Subsystem for Linux (WSL or WSL2). |
81
82
  | `on_termux()` | Returns `True` if running in the Termux Android environment. |
82
83
  | `on_freebsd()` | Returns `True` on FreeBSD. |
83
84
  | `on_ish_alpine()` | Returns `True` if running in the iSH Alpine Linux iOS emulator. |
84
85
  | `on_android()` | Returns `True` on any Android-based Linux environment. |
86
+ | `on_pydroid()` | Returns `True` Return True if running under the Pydroid 3 Android app (other versions untested). |
85
87
  | `in_repl()` | Returns `True` is the user is currently in a Python REPL; hasattr(sys,'ps1'). |
86
88
 
87
89
  ### Packaging and Build Checking
@@ -118,7 +120,7 @@ Key Question: "What could I do next?"
118
120
  | Function | Description |
119
121
  | :--- | :--- |
120
122
  | `edit_textfile(path)` | Opens a text file for editing using the default editor (Windows, Linux, macOS) or nano in Termux/iSH. In REPL mode, prints an error. Path argument (str or Path) uses Path.resolve() for stability. |
121
- | `interp_path(print_path=False)` | Returns the path to the Python interpreter binary (sys.executable). Optionally prints the path. Returns empty string if unavailable. |
123
+ | `interp_path()` | Returns the path to the Python interpreter binary (sys.executable). Returns empty string if unavailable. |
122
124
  | `main()` | Prints a comprehensive environment report with sections: Interpreter Checks (sys.executable), Current Environment Check (sys.argv[0]), Current Build Checks (sys attributes), Operating System Checks (platform.system()), and Capability Checks. Run via `python -m pyhabitat` or `import pyhabitat; pyhabitat.main()` in the REPL. |
123
125
 
124
126
  </details>
@@ -0,0 +1,11 @@
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=YlrcmovQBa7JapQLCqG9R6pjmYElL-8oVUHVSXH4MLw,33007
6
+ pyhabitat-1.0.20.dist-info/licenses/LICENSE,sha256=D4fg30ctUGnCJlWu3ONv5-V8JE1v3ctakoJTcVjsJlg,1072
7
+ pyhabitat-1.0.20.dist-info/METADATA,sha256=1MXhoZ1tOLB_tm1JHkH21-vubm3QWtlSW4os7GTB_WE,10900
8
+ pyhabitat-1.0.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ pyhabitat-1.0.20.dist-info/entry_points.txt,sha256=409xZ-BrarQJJLtO-aActCGkL0FMhNVi9wsq3u7tRHM,52
10
+ pyhabitat-1.0.20.dist-info/top_level.txt,sha256=zXYK44Qu8EqxUETREvd2diMUaB5JiGRErkwFaoLQnnI,10
11
+ pyhabitat-1.0.20.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- pyhabitat/__init__.py,sha256=GcpHBpfEer9O9dKBUREU1RTd5w0im9ph5ew4bs7jOvY,1228
2
- pyhabitat/__main__.py,sha256=hhH17lkw-ZalKp9NolnPGwW0KYxbXirspWvhBKNyBks,67
3
- pyhabitat/__main__stable.py,sha256=UACpHLrr_Rmf0L5dJCEae6kFzLn7dqCqIri68IBnb10,2910
4
- pyhabitat/cli.py,sha256=vuRczazuumIaJl4Td2VvCR5lXcKKNH90V8Yx0KZUJck,663
5
- pyhabitat/environment.py,sha256=7yXMCHvTRkfwwQnsgzrDXPDG9m8h8yhI5oCNlJTHWUA,28026
6
- pyhabitat-1.0.18.dist-info/licenses/LICENSE,sha256=D4fg30ctUGnCJlWu3ONv5-V8JE1v3ctakoJTcVjsJlg,1072
7
- pyhabitat-1.0.18.dist-info/METADATA,sha256=Xk4blTODZGW6NiuqN5e33SuePkTb-PnNU5pGGzeHXvs,10733
8
- pyhabitat-1.0.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- pyhabitat-1.0.18.dist-info/entry_points.txt,sha256=409xZ-BrarQJJLtO-aActCGkL0FMhNVi9wsq3u7tRHM,52
10
- pyhabitat-1.0.18.dist-info/top_level.txt,sha256=zXYK44Qu8EqxUETREvd2diMUaB5JiGRErkwFaoLQnnI,10
11
- pyhabitat-1.0.18.dist-info/RECORD,,