lanscape 1.2.7a3__py3-none-any.whl → 1.2.8__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 lanscape might be problematic. Click here for more details.

@@ -7,20 +7,16 @@ from typing import Any, Dict
7
7
  class RuntimeArgs:
8
8
  reloader: bool = False
9
9
  port: int = 5001
10
- nogui: bool = False
11
10
  logfile: bool = False
12
11
  loglevel: str = 'INFO'
13
- headless: bool = False
14
12
 
15
13
  def parse_args() -> RuntimeArgs:
16
14
  parser = argparse.ArgumentParser(description='LANscape')
17
15
 
18
16
  parser.add_argument('--reloader', action='store_true', help='Use flask\'s reloader (helpful for local development)')
19
17
  parser.add_argument('--port', type=int, default=5001, help='Port to run the webserver on')
20
- parser.add_argument('--nogui', action='store_true', help='Run in standalone mode')
21
18
  parser.add_argument('--logfile', action='store_true', help='Log output to lanscape.log')
22
- parser.add_argument('--loglevel', default='INFO', choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], help='Set the log level')
23
- parser.add_argument('--headless', action="store_true",help="Similar to nogui, but doesnt try to open a browser. Good for running in a container.")
19
+ parser.add_argument('--loglevel', default='INFO', help='Set the log level')
24
20
 
25
21
  # Parse the arguments
26
22
  args = parser.parse_args()
@@ -33,5 +29,12 @@ def parse_args() -> RuntimeArgs:
33
29
  # Only pass arguments that exist in the Args dataclass
34
30
  filtered_args = {name: args_dict[name] for name in field_names if name in args_dict}
35
31
 
32
+ # Deal with loglevel formatting
33
+ filtered_args['loglevel'] = filtered_args['loglevel'].upper()
34
+
35
+ valid_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
36
+ if filtered_args['loglevel'] not in valid_levels:
37
+ raise ValueError(f"Invalid log level: {filtered_args['loglevel']}. Must be one of: {valid_levels}")
38
+
36
39
  # Return the dataclass instance with the dynamically assigned values
37
40
  return RuntimeArgs(**filtered_args)
@@ -16,7 +16,9 @@ latest = None # used to 'remember' pypi version each runtime
16
16
  def is_update_available(package=PACKAGE) -> bool:
17
17
  installed = get_installed_version(package)
18
18
  available = lookup_latest_version(package)
19
- if installed == LOCAL_VERSION: return False #local
19
+ if installed == LOCAL_VERSION: return False # local
20
+ if 'a' in installed: return False # alpha
21
+ if 'b' in installed: return False # beta
20
22
 
21
23
  return installed != available
22
24
 
@@ -1,5 +1,3 @@
1
1
  from .test_api import ApiTestCase
2
2
  from .test_env import EnvTestCase
3
3
  from .test_library import LibraryTestCase
4
- from .test_webview import WebViewTestCase
5
-
@@ -1,10 +1,7 @@
1
1
 
2
2
  from ..libraries.ip_parser import get_address_count
3
- import webview, time
4
3
 
5
- import warnings
6
4
 
7
- from concurrent.futures import ThreadPoolExecutor, Future
8
5
  def right_size_subnet(subnet: str):
9
6
  """
10
7
  Used to improve speed of test time
@@ -16,45 +13,3 @@ def right_size_subnet(subnet: str):
16
13
  mask += 1
17
14
  return right_size_subnet(f"{ip}/{mask}")
18
15
  return subnet
19
-
20
- def webview_client(title, url):
21
- def decorator(func):
22
- def wrapper(*args, **kwargs):
23
- baseclass = args[0]
24
-
25
- window = webview.create_window(title, url)
26
-
27
- # Create a Future object to communicate results/exceptions
28
- future = Future()
29
-
30
- # Define the function to run in the secondary thread
31
- def test_function():
32
- # disable resource warning occuring in py >=3.13
33
- warnings.filterwarnings("ignore", category=ResourceWarning)
34
-
35
- try:
36
- # Call the decorated function
37
- func(baseclass, window, **kwargs)
38
- future.set_result("Completed Successfully")
39
- except Exception as e:
40
- # Set the exception in the Future
41
- future.set_exception(e)
42
- finally:
43
- # Close the WebView window
44
- if window is not None:
45
- window.destroy()
46
-
47
- # Define a function to start the thread
48
- def start_test_function():
49
- # Run the function in a secondary thread
50
- with ThreadPoolExecutor() as executor:
51
- executor.submit(test_function)
52
-
53
- # Start the WebView and execute the thread alongside it
54
- webview.start(start_test_function)
55
-
56
- # Retrieve the result or exception from the Future
57
- future.result()
58
-
59
- return wrapper
60
- return decorator
lanscape/ui/app.py CHANGED
@@ -97,13 +97,14 @@ def start_webserver_dameon(args: RuntimeArgs) -> multiprocessing.Process:
97
97
  sleep(2)
98
98
 
99
99
  def start_webserver(args: RuntimeArgs) -> int:
100
- app.run(
101
- host='0.0.0.0',
102
- port=args.port,
103
- debug=args.reloader,
104
- use_reloader=args.reloader
105
- )
106
-
100
+ run_args = {
101
+ 'host':'0.0.0.0',
102
+ 'port':args.port,
103
+ 'debug':args.reloader,
104
+ 'use_reloader':args.reloader
105
+ }
106
+
107
+ app.run(**run_args)
107
108
 
108
109
  if __name__ == "__main__":
109
110
  start_webserver(True)
lanscape/ui/main.py CHANGED
@@ -12,8 +12,8 @@ args = parse_args()
12
12
  configure_logging(args.loglevel, args.logfile)
13
13
 
14
14
  from ..libraries.version_manager import get_installed_version, is_update_available
15
- from .webviewer import start_webview
16
15
  from .app import start_webserver
16
+ import socket
17
17
 
18
18
 
19
19
  log = logging.getLogger('core')
@@ -28,24 +28,23 @@ def main():
28
28
  if not IS_FLASK_RELOAD:
29
29
  log.info(f'LANscape v{get_installed_version()}')
30
30
  try_check_update()
31
- else:
31
+
32
+ else:
32
33
  log.info('Flask reloaded app.')
33
34
 
35
+ args.port = get_valid_port(args.port)
36
+
34
37
 
35
38
  try:
36
- if args.nogui or args.headless:
37
- no_gui(args)
38
- else:
39
- start_webview(
40
- args
41
- )
39
+
40
+ no_gui(args)
41
+
42
42
  log.info('Exiting...')
43
43
  except Exception:
44
44
  # showing error in debug only because this is handled gracefully
45
- log.debug('Failed to start webview client. Traceback below')
45
+ log.debug('Failed to start. Traceback below')
46
46
  log.debug(traceback.format_exc())
47
- if not args.nogui:
48
- log.error('Unable to start webview client. Try running with flag --nogui')
47
+
49
48
 
50
49
 
51
50
  def try_check_update():
@@ -68,23 +67,31 @@ def open_browser(url: str,wait=2):
68
67
  time.sleep(wait)
69
68
  webbrowser.open(url, new=2)
70
69
  except:
71
- srv_url = f"0.0.0.0:{url.split(':')[1]}"
72
70
  log.debug(traceback.format_exc())
73
- log.info(f'unable to open web browser, server running on {srv_url}')
71
+ log.info(f'Unable to open web browser, server running on {url}')
74
72
 
75
73
  threading.Thread(target=do_open).start()
76
74
 
77
75
  def no_gui(args: RuntimeArgs):
78
76
  # determine if it was reloaded by flask debug reloader
79
77
  # if it was, dont open the browser again
80
- if not IS_FLASK_RELOAD and not args.headless:
78
+ if not IS_FLASK_RELOAD:
81
79
  open_browser(f'http://127.0.0.1:{args.port}')
82
- if args.headless:
83
- log.info(f'Started in headless mode on: http://127.0.0.1:{args.port}')
80
+ log.info(f'Flask started: http://127.0.0.1:{args.port}')
81
+
84
82
  start_webserver(
85
83
  args
86
84
  )
87
85
 
86
+ def get_valid_port(port: int):
87
+ """
88
+ Get the first available port starting from the specified port
89
+ """
90
+ while True:
91
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
92
+ if s.connect_ex(('localhost', port)) != 0:
93
+ return port
94
+ port += 1
88
95
 
89
96
  if __name__ == "__main__":
90
97
  main()
@@ -460,8 +460,15 @@ input[type="range"]::-moz-range-thumb {
460
460
  }
461
461
 
462
462
  .text-secondary {
463
- color: var(--secondary-accent)
463
+ color: var(--secondary-accent) !important;
464
464
  }
465
+ .text-danger {
466
+ color: var(--text-danger-color) !important;
467
+ }
468
+ .text-info {
469
+ color: var(--text-accent-color) !important;
470
+ }
471
+
465
472
  .secondary-icon-btn {
466
473
  color: var(--secondary-accent);
467
474
  transition: .2s all ease-in-out;
@@ -11,10 +11,15 @@
11
11
  <div class="version">
12
12
  v{{app_version}}
13
13
  {% if is_local %}
14
- <span>(Local Run)</span>
14
+ <span class="text-info">(Local)</span>
15
15
  {% elif update_available %}
16
16
  <span>(Update Available)</span>
17
- {% endif %}
17
+ {% endif %}
18
+ {% if 'a' in app_version %}
19
+ <span class="text-danger">(Alpha)</span>
20
+ {% elif 'b' in app_version %}
21
+ <span class="text-info">(Beta)</span>
22
+ {% endif %}
18
23
  </div>
19
24
 
20
25
  <div id="app-actions">
@@ -28,7 +33,6 @@
28
33
  </span>
29
34
  </a>
30
35
 
31
- {% if runtime_args.get('nogui') or runtime_args.get('headless') %}
32
36
  <a href="/shutdown-ui">
33
37
  <span
34
38
  id="power-button"
@@ -39,7 +43,6 @@
39
43
  power_settings_new
40
44
  </span>
41
45
  </a>
42
- {% endif %}
43
46
  </div>
44
47
 
45
48
  </footer>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lanscape
3
- Version: 1.2.7a3
3
+ Version: 1.2.8
4
4
  Summary: A python based local network scanner
5
5
  Author-email: Michael Dennis <michael@dipduo.com>
6
6
  Project-URL: Homepage, https://github.com/mdennis281/py-lanscape
@@ -15,7 +15,6 @@ Requires-Dist: Flask<5.0,>=3.0
15
15
  Requires-Dist: psutil<7.0,>=6.0
16
16
  Requires-Dist: requests<3.0,>=2.32
17
17
  Requires-Dist: setuptools
18
- Requires-Dist: pywebview<6.0,>=4.2
19
18
  Requires-Dist: scapy<3.0,>=2.3.2
20
19
  Requires-Dist: tabulate==0.9.0
21
20
  Requires-Dist: pytest
@@ -33,17 +32,15 @@ python -m lanscape
33
32
 
34
33
  ## Flags
35
34
  - `--port <port number>` port of the flask app (default: 5001)
36
- - `--nogui` run in web mode (default: false)
37
35
  - `--reloader` essentially flask debug mode- good for local development (default: false)
38
36
  - `--logfile` save log output to lanscape.log
39
37
  - `--loglevel <level>` set the logger's log level (default: INFO)
40
- - `--headless` similar to nogui but doesnt try to open a browser (default: false)
41
38
 
42
39
 
43
40
  Examples:
44
41
  ```shell
45
42
  python -m lanscape --reloader
46
- python -m lanscape --nogui --port 5002
43
+ python -m lanscape --port 5002
47
44
  python -m lanscape --logfile --loglevel DEBUG
48
45
  ```
49
46
 
@@ -8,9 +8,9 @@ lanscape/libraries/logger.py,sha256=hGbnj1cTGZT5elwdXUBVyLg_E2Pnocs2IfWumUkHj7M,
8
8
  lanscape/libraries/mac_lookup.py,sha256=jejM0NHLbxB9G2hfge2qt0qcNgnSlZERbiTP-RLUUPA,2141
9
9
  lanscape/libraries/net_tools.py,sha256=zWWvKon46i3ImhaGg2IpCV-Cx01-jFBBZCIHHHyFmqg,10112
10
10
  lanscape/libraries/port_manager.py,sha256=fNext3FNfGnGYRZK9RhTEwQ2K0e0YmmMlhK4zVAvoCw,1977
11
- lanscape/libraries/runtime_args.py,sha256=Ksu9xb4zyVl2RRBSus_-c9UrbU5A3XFmDxHrUeTUwdw,1670
11
+ lanscape/libraries/runtime_args.py,sha256=JJTzWgQ-0aRh7Ce5efIwBmHZv1LfWjFagtNUNlLN7Uo,1660
12
12
  lanscape/libraries/subnet_scan.py,sha256=2BW69tbntqEujiqsdlQruLHfoAwnFrv6OLMhvQWauYM,11191
13
- lanscape/libraries/version_manager.py,sha256=MQ9hOZqiK_fe0jQR9FsbiSW52zKpryPFT6p8fQdKWTM,1505
13
+ lanscape/libraries/version_manager.py,sha256=wdB5DPW8IjHJfgvT4mUKz7ndEV922WgVEqbwd6jfun4,1599
14
14
  lanscape/resources/mac_addresses/convert_csv.py,sha256=w3Heed5z2mHYDEZNBep3_hNg4dbrp_N6J54MGxnrq4s,721
15
15
  lanscape/resources/mac_addresses/mac_db.json,sha256=ygtFSwNwJzDlg6hmAujdgCyzUjxt9Di75J8SO4xYIs8,2187804
16
16
  lanscape/resources/ports/convert_csv.py,sha256=mWe8zucWVfnlNEx_ZzH5Vc3tJJbdi-Ih4nm2yKNrRN0,720
@@ -18,15 +18,13 @@ lanscape/resources/ports/full.json,sha256=Abfbi-b5yZF4jR5NS6CT6QpIDfx4Vk04gIC1fK
18
18
  lanscape/resources/ports/large.json,sha256=jK4gkzlPT74uv4y0TvFjsaOaUcX7Cy8Zxe2bh5FYcc0,144496
19
19
  lanscape/resources/ports/medium.json,sha256=3yHAca0_pEWXK4k1wmda1eNVM6ftzcpKn5VspVwmkRs,3667
20
20
  lanscape/resources/ports/small.json,sha256=Mj3zGVG1F2eqZx2YkrLpTL8STeLcqB8_65IR67MS1Tg,397
21
- lanscape/tests/__init__.py,sha256=oFe5tRDmeHd8ynBSEXIaN7I1pwN0_Bvvoks_ptoLBME,158
22
- lanscape/tests/_helpers.py,sha256=jXD9Bnoxb8Hhwne3Algqt1mAAysB1GYA7CTh24ECpqM,2028
21
+ lanscape/tests/__init__.py,sha256=xYPeceOF-ppTS0wnq7CkVYQMwupmeSHxbYLbGj_imZ0,113
22
+ lanscape/tests/_helpers.py,sha256=wXJfUwzL3Fq4XBsC3dValCbXsf0U8FisuM_yo1de4QQ,371
23
23
  lanscape/tests/test_api.py,sha256=fIVIA6O9ssPRjofTHLS6z7XPNTkvv2rl2jDaxVCjFGU,5669
24
24
  lanscape/tests/test_env.py,sha256=ivFhCcemJ9vbe0_KtUkbqDY4r9nsDB8rVLUVjV-sNj8,673
25
25
  lanscape/tests/test_library.py,sha256=u-UBx76Dyh2xLhiCNxnzv7lIsWoUGKUtbPqkVUg7xwE,1552
26
- lanscape/tests/test_webview.py,sha256=2KQ7pr05AJ8DbaXkpA2FngRP0-UYIpjIwIYpcddBhAE,605
27
- lanscape/ui/app.py,sha256=pY-wZIGsqARWgFigpdLe16TdfOwGdN1EfWmvA7Ew9M8,3151
28
- lanscape/ui/main.py,sha256=DZZiFCZUZUxK6_tXIrRTe5-pG_DLBGHqazvcM20Q4rY,2703
29
- lanscape/ui/webviewer.py,sha256=BuD17wwUzRdpL4LsEb4cUVSBFr0CZirqkTQ46coUVj4,482
26
+ lanscape/ui/app.py,sha256=hGmzoyH4YW2WmQsqZ6v-9r5DYzWi2KYP6_toZSxQqFg,3186
27
+ lanscape/ui/main.py,sha256=HMQythH0fijX1Mt9fGQBcqmY37dQQ19ROC99JbCNAvs,2701
30
28
  lanscape/ui/blueprints/__init__.py,sha256=agvgPOSVbrxddaw6EY64ZZr1CQi1Qzwcs1t0lZMv5oY,206
31
29
  lanscape/ui/blueprints/api/__init__.py,sha256=t0QOq3vHFWmlZm_3YFPQbQzCn1a_a5cmRchtIxwy4eY,103
32
30
  lanscape/ui/blueprints/api/port.py,sha256=2UA38umzXE8pMitx1E-_wJHyL1dYYbtM6Kg5zVtfj6A,1019
@@ -35,8 +33,7 @@ lanscape/ui/blueprints/api/tools.py,sha256=CD0NDSX8kN6_lpl0jEw-ULLsDx7pKODCMFQia
35
33
  lanscape/ui/blueprints/web/__init__.py,sha256=-WRjENG8D99NfaiSDk9uAa8OX6XJq9Zmq1ck29ARL-w,92
36
34
  lanscape/ui/blueprints/web/routes.py,sha256=9S-xDBnUR-8Wf-B0fipBNFeTz-xiHhHy0WmhW9qWqZk,2166
37
35
  lanscape/ui/static/lanscape.webmanifest,sha256=0aauJk_Bybd0B2iwzJfvPcs7AX43kVHs0dtpV6_jSWk,459
38
- lanscape/ui/static/css/style.css,sha256=UECPZTx2vm7Ntgvm3dAaCq8sFyG_xmkx2HgorSa7rIs,15653
39
- lanscape/ui/static/img/readme1.png,sha256=JrvCM5ZZoJ9YvY1W1nUIXcw4su4dmlhTY-o-iIZDvkc,39347
36
+ lanscape/ui/static/css/style.css,sha256=LbQ4O98uttwY2Msxv9XnniOTzZApGsqkFg8PCoe1ZcQ,15801
40
37
  lanscape/ui/static/img/ico/android-chrome-192x192.png,sha256=DxM2E9GjpKX-hSaSmAoW0GxLJ2fdXKJ-WOgoxYlDybw,24130
41
38
  lanscape/ui/static/img/ico/android-chrome-512x512.png,sha256=UoL_8UdhGU1q2_XkJ2T7QojgcNnz7FrBChlVRaMOWIs,73788
42
39
  lanscape/ui/static/img/ico/apple-touch-icon.png,sha256=Yhzhva3J_wjnC3zk2e7GXhEVNLozGVE_IjQkg4R5Ip4,21664
@@ -51,7 +48,7 @@ lanscape/ui/static/js/quietReload.js,sha256=_mHzpUsGL4Lm1hNsr3VYSOGVcgGA2y1-eZHa
51
48
  lanscape/ui/static/js/shutdown-server.js,sha256=WkO7_SNSHM_6kReUoCoExIdCf7Sl7IPiSiNxpbI-r0s,469
52
49
  lanscape/ui/static/js/subnet-info.js,sha256=aytt0LkBx4FVq36TxiMEw3aM7XQLHg_ng1U2WDwZVF4,577
53
50
  lanscape/ui/static/js/subnet-selector.js,sha256=OG01pDaSOPLq3Ial0aO0CqPcob9tPZA1MZKGmQG0W7Q,366
54
- lanscape/ui/templates/base.html,sha256=i6Gz4l2wSuByGqkKmQBCMYVlezH2J4nS7zU1G4KTMEg,1368
51
+ lanscape/ui/templates/base.html,sha256=P5xnMlvDXYkYSXdSZUWaRfhsszNuZPP7A56hemBrAFs,1498
55
52
  lanscape/ui/templates/error.html,sha256=zXFO0zPIfQORWq1ZMiSZ8G7FjfhVVr-aaYC0HeBl4Rs,1068
56
53
  lanscape/ui/templates/info.html,sha256=oCC59keGEfgUB4WaCozaeZEfNb8Nr7y61DmkRBMqs18,2461
57
54
  lanscape/ui/templates/main.html,sha256=M12xJSN6Ga565vIPhdCiqcr1tYgDrqzuQTeuXtk-8yo,3759
@@ -64,8 +61,8 @@ lanscape/ui/templates/scan/ip-table-row.html,sha256=ptY24rxJRaA4PEEQRDncaq6Q0ql5
64
61
  lanscape/ui/templates/scan/ip-table.html,sha256=ds__UP9JiTKf5IxCmTMzw--eN_yg1Pvn3Nj1KvQxeZg,940
65
62
  lanscape/ui/templates/scan/overview.html,sha256=Q0gmkVI-xNLZ-zWA9qm4H14cv_eF_bQs1KYPyAaoHLY,1141
66
63
  lanscape/ui/templates/scan/scan-error.html,sha256=Q4eZM5ThrxnFaWOSTUpK8hA2ksHwhxOBTaVUCLALhyA,1032
67
- lanscape-1.2.7a3.dist-info/LICENSE,sha256=cCO-NbS01Ilwc6djHjZ7LIgPFRkRmWdr0fH2ysXKioA,1090
68
- lanscape-1.2.7a3.dist-info/METADATA,sha256=BfD04zXE0DAEm0wHdiGEZ2g-6TpbSO1i5opabklOhN8,2619
69
- lanscape-1.2.7a3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
70
- lanscape-1.2.7a3.dist-info/top_level.txt,sha256=E9D4sjPz_6H7c85Ycy_pOS2xuv1Wm-ilKhxEprln2ps,9
71
- lanscape-1.2.7a3.dist-info/RECORD,,
64
+ lanscape-1.2.8.dist-info/LICENSE,sha256=cCO-NbS01Ilwc6djHjZ7LIgPFRkRmWdr0fH2ysXKioA,1090
65
+ lanscape-1.2.8.dist-info/METADATA,sha256=758DXrtDG7mYKA-iEduUgi7Zw5hiaNCG5ZvON5jdZe4,2442
66
+ lanscape-1.2.8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
67
+ lanscape-1.2.8.dist-info/top_level.txt,sha256=E9D4sjPz_6H7c85Ycy_pOS2xuv1Wm-ilKhxEprln2ps,9
68
+ lanscape-1.2.8.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- import webview
2
- from ._helpers import webview_client
3
- import unittest
4
- from time import sleep
5
-
6
-
7
-
8
- class WebViewTestCase(unittest.TestCase):
9
-
10
- @webview_client('Test Window','https://google.com')
11
- def test_webview_launch(self, client: webview.Window):
12
- # This test checks that the window has been created successfully
13
- sleep(2)
14
- self.assertIsNotNone(client,msg="WebView window failed to create.")
15
- self.assertEqual(client.title,'Test Window',msg='WebView window title did not match.')
16
- self.assertIn('google.com',client.real_url,msg=f'URL mismatch')
Binary file
lanscape/ui/webviewer.py DELETED
@@ -1,20 +0,0 @@
1
- import webview
2
- from .app import start_webserver_dameon
3
- from ..libraries.runtime_args import RuntimeArgs
4
-
5
-
6
-
7
- def start_webview(args: RuntimeArgs) -> None:
8
- # Start Flask server in a separate thread
9
- start_webserver_dameon(args)
10
-
11
- # Start the Pywebview window
12
- webview.create_window('LANscape', f'http://127.0.0.1:{args.port}')
13
- webview.start()
14
-
15
-
16
-
17
- if __name__ == "__main__":
18
- # Start Flask server in a separate thread
19
- start_webview(True)
20
-