neuronum 5.6.0__py3-none-any.whl → 5.8.0__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 neuronum might be problematic. Click here for more details.

cli/main.py CHANGED
@@ -10,6 +10,8 @@ import click
10
10
  import questionary
11
11
  from pathlib import Path
12
12
  import requests
13
+ import psutil
14
+ from datetime import datetime
13
15
 
14
16
 
15
17
  @click.group()
@@ -251,9 +253,13 @@ def delete_cell():
251
253
  @click.option('--stream', multiple=True, default=None, help="Optional stream ID for stream.")
252
254
  @click.option('--app', is_flag=True, help="Generate a Node with app template")
253
255
  def init_node(sync, stream, app):
254
- asyncio.run(async_init_node(sync, stream, app))
256
+ descr = click.prompt("Node description: Type up to 25 characters").strip()
257
+ if descr and len(descr) > 25:
258
+ click.echo("Description too long. Max 25 characters allowed.")
259
+ return
260
+ asyncio.run(async_init_node(sync, stream, app, descr))
255
261
 
256
- async def async_init_node(sync, stream, app):
262
+ async def async_init_node(sync, stream, app, descr):
257
263
  credentials_folder_path = Path.home() / ".neuronum"
258
264
  env_path = credentials_folder_path / ".env"
259
265
 
@@ -278,11 +284,16 @@ async def async_init_node(sync, stream, app):
278
284
  return
279
285
 
280
286
  url = f"https://{network}/api/init_node"
281
- node_payload = {"host": host, "password": password, "synapse": synapse}
287
+ node = {
288
+ "host": host,
289
+ "password": password,
290
+ "synapse": synapse,
291
+ "descr": descr,
292
+ }
282
293
 
283
294
  async with aiohttp.ClientSession() as session:
284
295
  try:
285
- async with session.post(url, json=node_payload) as response:
296
+ async with session.post(url, json=node) as response:
286
297
  response.raise_for_status()
287
298
  data = await response.json()
288
299
  nodeID = data["nodeID"]
@@ -565,13 +576,38 @@ asyncio.run(main())
565
576
  @click.command()
566
577
  @click.option('--d', is_flag=True, help="Start node in detached mode")
567
578
  def start_node(d):
579
+ pid_file = Path.cwd() / "status.txt"
580
+ system_name = platform.system()
581
+ active_pids = []
582
+
583
+ start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
584
+
585
+ if pid_file.exists():
586
+ try:
587
+ with open(pid_file, "r") as f:
588
+ pids = [int(line.strip()) for line in f if line.strip().isdigit()]
589
+ for pid in pids:
590
+ if system_name == "Windows":
591
+ if psutil.pid_exists(pid):
592
+ active_pids.append(pid)
593
+ else:
594
+ try:
595
+ os.kill(pid, 0)
596
+ active_pids.append(pid)
597
+ except OSError:
598
+ continue
599
+ except Exception as e:
600
+ click.echo(f"Failed to read PID file: {e}")
601
+
602
+ if active_pids:
603
+ click.echo(f"Node is already running. Active PIDs: {', '.join(map(str, active_pids))}")
604
+ return
605
+
568
606
  click.echo("Starting Node...")
569
607
 
570
608
  project_path = Path.cwd()
571
609
  script_files = glob.glob("sync_*.py") + glob.glob("stream_*.py") + glob.glob("app.py")
572
-
573
610
  processes = []
574
- system_name = platform.system()
575
611
 
576
612
  for script in script_files:
577
613
  script_path = project_path / script
@@ -587,7 +623,10 @@ def start_node(d):
587
623
  start_new_session=True
588
624
  )
589
625
  else:
590
- process = subprocess.Popen(["python", str(script_path)], start_new_session=True)
626
+ process = subprocess.Popen(
627
+ [python_cmd, str(script_path)],
628
+ start_new_session=True
629
+ )
591
630
 
592
631
  processes.append(process.pid)
593
632
 
@@ -595,55 +634,172 @@ def start_node(d):
595
634
  click.echo("Error: No valid node script found. Ensure the node is set up correctly.")
596
635
  return
597
636
 
598
- with open("node_pid.txt", "w") as f:
637
+ with open(pid_file, "w") as f:
638
+ f.write(f"Started at: {start_time}\n")
599
639
  f.write("\n".join(map(str, processes)))
600
640
 
601
- click.echo("Node started successfully!")
641
+ click.echo(f"Node started successfully with PIDs: {', '.join(map(str, processes))}")
602
642
 
603
643
 
604
644
  @click.command()
605
- def stop_node():
606
- asyncio.run(async_stop_node())
645
+ def check_node():
646
+ click.echo("Checking Node status...")
607
647
 
608
- async def async_stop_node():
609
- click.echo("Stopping Node...")
648
+ env_data = {}
649
+ try:
650
+ with open(".env", "r") as f:
651
+ for line in f:
652
+ if "=" in line:
653
+ key, value = line.strip().split("=", 1)
654
+ env_data[key] = value
655
+ nodeID = env_data.get("NODE", "")
656
+ except FileNotFoundError:
657
+ click.echo("Error: .env with credentials not found")
658
+ return
659
+ except Exception as e:
660
+ click.echo(f"Error reading .env file: {e}")
661
+ return
662
+
663
+ pid_file = Path.cwd() / "status.txt"
610
664
 
611
- node_pid_path = Path("node_pid.txt")
665
+ if not pid_file.exists():
666
+ click.echo(f"Node {nodeID} is not running. Status file missing.")
667
+ return
612
668
 
613
669
  try:
614
- with open("node_pid.txt", "r") as f:
615
- pids = [int(pid.strip()) for pid in f.readlines()]
670
+ with open(pid_file, "r") as f:
671
+ lines = f.readlines()
672
+ timestamp_line = next((line for line in lines if line.startswith("Started at:")), None)
673
+ pids = [int(line.strip()) for line in lines if line.strip().isdigit()]
674
+
675
+ if timestamp_line:
676
+ click.echo(timestamp_line.strip())
677
+ start_time = datetime.strptime(timestamp_line.split(":", 1)[1].strip(), "%Y-%m-%d %H:%M:%S")
678
+ now = datetime.now()
679
+ uptime = now - start_time
680
+ click.echo(f"Uptime: {str(uptime).split('.')[0]}")
681
+ except Exception as e:
682
+ click.echo(f"Failed to read PID file: {e}")
683
+ return
616
684
 
617
- system_name = platform.system()
685
+ system_name = platform.system()
686
+ running_pids = []
618
687
 
619
- for pid in pids:
688
+ for pid in pids:
689
+ if system_name == "Windows":
690
+ if psutil.pid_exists(pid):
691
+ running_pids.append(pid)
692
+ else:
620
693
  try:
694
+ os.kill(pid, 0)
695
+ running_pids.append(pid)
696
+ except OSError:
697
+ continue
698
+
699
+ if running_pids:
700
+ for pid in running_pids:
701
+ proc = psutil.Process(pid)
702
+ mem = proc.memory_info().rss / (1024 * 1024) # in MB
703
+ cpu = proc.cpu_percent(interval=0.1)
704
+ click.echo(f"PID {pid} → Memory: {mem:.2f} MB | CPU: {cpu:.1f}%")
705
+ click.echo(f"Node {nodeID} is running. Active PIDs: {', '.join(map(str, running_pids))}")
706
+ else:
707
+ click.echo(f"Node {nodeID} is not running.")
708
+
709
+
710
+ @click.command()
711
+ @click.option('--d', is_flag=True, help="Restart node in detached mode")
712
+ def restart_node(d):
713
+ pid_file = Path.cwd() / "status.txt"
714
+ system_name = platform.system()
715
+
716
+ start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
717
+
718
+ env_data = {}
719
+ try:
720
+ with open(".env", "r") as f:
721
+ for line in f:
722
+ key, value = line.strip().split("=")
723
+ env_data[key] = value
724
+
725
+ nodeID = env_data.get("NODE", "")
726
+
727
+ except FileNotFoundError:
728
+ print("Error: .env with credentials not found")
729
+ return
730
+ except Exception as e:
731
+ print(f"Error reading .env file: {e}")
732
+ return
733
+
734
+ if pid_file.exists():
735
+ try:
736
+ with open(pid_file, "r") as f:
737
+ pids = [int(line.strip()) for line in f if line.strip().isdigit()]
738
+
739
+ for pid in pids:
621
740
  if system_name == "Windows":
622
- await asyncio.to_thread(subprocess.run, ["taskkill", "/F", "/PID", str(pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
741
+ if psutil.pid_exists(pid):
742
+ proc = psutil.Process(pid)
743
+ proc.terminate()
623
744
  else:
624
- await asyncio.to_thread(os.kill, pid, 9)
625
- except ProcessLookupError:
626
- click.echo(f"Warning: Process {pid} already stopped or does not exist.")
745
+ try:
746
+ os.kill(pid, 15)
747
+ except OSError:
748
+ continue
627
749
 
628
- await asyncio.to_thread(os.remove, node_pid_path)
629
- click.echo("Node stopped successfully!")
750
+ pid_file.unlink()
630
751
 
631
- except FileNotFoundError:
632
- click.echo("Error: No active node process found.")
633
- except subprocess.CalledProcessError:
634
- click.echo("Error: Unable to stop some node processes.")
752
+ click.echo(f"Terminated existing {nodeID} processes: {', '.join(map(str, pids))}")
753
+
754
+ except Exception as e:
755
+ click.echo(f"Failed to terminate processes: {e}")
756
+ return
757
+ else:
758
+ click.echo(f"Node {nodeID} is not running")
759
+
760
+ click.echo(f"Starting Node {nodeID}...")
761
+ project_path = Path.cwd()
762
+ script_files = glob.glob("sync_*.py") + glob.glob("stream_*.py") + glob.glob("app.py")
763
+ processes = []
764
+
765
+ python_cmd = "pythonw" if system_name == "Windows" else "python"
766
+
767
+ for script in script_files:
768
+ script_path = project_path / script
769
+ if script_path.exists():
770
+ if d:
771
+ process = subprocess.Popen(
772
+ ["nohup", python_cmd, str(script_path), "&"] if system_name != "Windows"
773
+ else [python_cmd, str(script_path)],
774
+ stdout=subprocess.DEVNULL,
775
+ stderr=subprocess.DEVNULL,
776
+ start_new_session=True
777
+ )
778
+ else:
779
+ process = subprocess.Popen([python_cmd, str(script_path)], start_new_session=True)
780
+
781
+ processes.append(process.pid)
782
+
783
+ if not processes:
784
+ click.echo("Error: No valid node script found.")
785
+ return
786
+
787
+ with open(pid_file, "w") as f:
788
+ f.write(f"Started at: {start_time}\n")
789
+ f.write("\n".join(map(str, processes)))
790
+
791
+ click.echo(f"Node {nodeID} started with new PIDs: {', '.join(map(str, processes))}")
635
792
 
636
793
 
637
794
  @click.command()
638
- def connect_node():
639
- node_type = questionary.select(
640
- "Choose Node type:",
641
- choices=["public", "private"]
642
- ).ask()
643
- descr = click.prompt("Node description (max. 25 characters)")
644
- asyncio.run(async_connect_node(descr, node_type))
795
+ def stop_node():
796
+ asyncio.run(async_stop_node())
797
+
798
+ async def async_stop_node():
799
+ click.echo("Stopping Node...")
800
+
801
+ node_pid_path = Path("status.txt")
645
802
 
646
- async def async_connect_node(descr, node_type):
647
803
  env_data = {}
648
804
  try:
649
805
  with open(".env", "r") as f:
@@ -652,10 +808,6 @@ async def async_connect_node(descr, node_type):
652
808
  env_data[key] = value
653
809
 
654
810
  nodeID = env_data.get("NODE", "")
655
- host = env_data.get("HOST", "")
656
- password = env_data.get("PASSWORD", "")
657
- network = env_data.get("NETWORK", "")
658
- synapse = env_data.get("SYNAPSE", "")
659
811
 
660
812
  except FileNotFoundError:
661
813
  print("Error: .env with credentials not found")
@@ -665,52 +817,46 @@ async def async_connect_node(descr, node_type):
665
817
  return
666
818
 
667
819
  try:
668
- with open("NODE.md", "r") as f:
669
- nodemd_file = f.read()
820
+ with open("status.txt", "r") as f:
821
+ pids = [int(line.strip()) for line in f if line.strip().isdigit()]
670
822
 
671
- except FileNotFoundError:
672
- print("Error: NODE.md file not found")
673
- return
674
- except Exception as e:
675
- print(f"Error reading NODE.md file: {e}")
676
- return
823
+ system_name = platform.system()
677
824
 
678
- url = f"https://{network}/api/connect_node/{node_type}"
825
+ for pid in pids:
826
+ try:
827
+ if system_name == "Windows":
828
+ await asyncio.to_thread(subprocess.run, ["taskkill", "/F", "/PID", str(pid)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
829
+ else:
830
+ await asyncio.to_thread(os.kill, pid, 9)
831
+ except ProcessLookupError:
832
+ click.echo(f"Warning: Process {pid} already stopped or does not exist.")
679
833
 
680
- node = {
681
- "nodeID": nodeID,
682
- "descr": descr,
683
- "host": host,
684
- "password": password,
685
- "synapse": synapse,
686
- "nodemd_file": nodemd_file
687
- }
834
+ await asyncio.to_thread(os.remove, node_pid_path)
835
+ click.echo(f"Node {nodeID} stopped successfully!")
688
836
 
689
- async with aiohttp.ClientSession() as session:
690
- try:
691
- async with session.post(url, json=node) as response:
692
- response.raise_for_status()
693
- data = await response.json()
694
- nodeID = data["nodeID"]
695
- node_url = data["node_url"]
696
- except aiohttp.ClientError as e:
697
- click.echo(f"Error sending request: {e}")
698
- return
699
-
700
- if nodeID == "Node does not exist":
701
- click.echo(f"Neuronum Node not found! Make sure you initialized your Node correctly")
702
- else:
703
- if node_type == "public":
704
- click.echo(f"Public Neuronum Node '{nodeID}' connected! Visit: {node_url}")
705
- else:
706
- click.echo(f"Private Neuronum Node '{nodeID}' connected!")
837
+ except FileNotFoundError:
838
+ click.echo("Error: No active node process found.")
839
+ except subprocess.CalledProcessError:
840
+ click.echo("Error: Unable to stop some node processes.")
707
841
 
708
842
 
709
843
  @click.command()
710
844
  def update_node():
711
- asyncio.run(async_update_node())
845
+ node_type = questionary.select(
846
+ "Update Node type:",
847
+ choices=["public", "private"]
848
+ ).ask()
849
+ descr = click.prompt(
850
+ "Update Node description: Type up to 25 characters, or press Enter to leave it unchanged",
851
+ default="None",
852
+ show_default=False
853
+ ).strip()
854
+ if descr and len(descr) > 25:
855
+ click.echo("Description too long. Max 25 characters allowed.")
856
+ return
857
+ asyncio.run(async_update_node(node_type, descr))
712
858
 
713
- async def async_update_node():
859
+ async def async_update_node(node_type: str, descr: str) -> None:
714
860
  env_data = {}
715
861
 
716
862
  try:
@@ -744,17 +890,19 @@ async def async_update_node():
744
890
  return
745
891
 
746
892
  url = f"https://{network}/api/update_node"
747
- node_payload = {
893
+ node = {
748
894
  "nodeID": nodeID,
749
895
  "host": host,
750
896
  "password": password,
751
897
  "synapse": synapse,
752
- "nodemd_file": nodemd_file
898
+ "node_type": node_type,
899
+ "nodemd_file": nodemd_file,
900
+ "descr": descr,
753
901
  }
754
902
 
755
903
  async with aiohttp.ClientSession() as session:
756
904
  try:
757
- async with session.post(url, json=node_payload) as response:
905
+ async with session.post(url, json=node) as response:
758
906
  response.raise_for_status()
759
907
  data = await response.json()
760
908
  nodeID = data["nodeID"]
@@ -782,54 +930,10 @@ async def async_update_node():
782
930
  await asyncio.to_thread(Path("streams.json").write_text, json.dumps(stx, indent=4))
783
931
  await asyncio.to_thread(Path("nodes.json").write_text, json.dumps(nodes, indent=4))
784
932
 
785
- click.echo(f"Neuronum Node '{nodeID}' updated! Visit: {node_url}")
786
-
787
-
788
- @click.command()
789
- def disconnect_node():
790
- asyncio.run(async_disconnect_node())
791
-
792
- async def async_disconnect_node():
793
- env_data = {}
794
-
795
- try:
796
- with open(".env", "r") as f:
797
- for line in f:
798
- key, value = line.strip().split("=")
799
- env_data[key] = value
800
-
801
- nodeID = env_data.get("NODE", "")
802
- host = env_data.get("HOST", "")
803
- password = env_data.get("PASSWORD", "")
804
- network = env_data.get("NETWORK", "")
805
- synapse = env_data.get("SYNAPSE", "")
806
-
807
- except FileNotFoundError:
808
- click.echo("Error: .env with credentials not found")
809
- return
810
- except Exception as e:
811
- click.echo(f"Error reading .env file: {e}")
812
- return
813
-
814
- url = f"https://{network}/api/disconnect_node"
815
- node_payload = {
816
- "nodeID": nodeID,
817
- "host": host,
818
- "password": password,
819
- "synapse": synapse
820
- }
821
-
822
- async with aiohttp.ClientSession() as session:
823
- try:
824
- async with session.post(url, json=node_payload) as response:
825
- response.raise_for_status()
826
- data = await response.json()
827
- nodeID = data["nodeID"]
828
- except aiohttp.ClientError as e:
829
- click.echo(f"Error sending request: {e}")
830
- return
831
-
832
- click.echo(f"Neuronum Node '{nodeID}' disconnected!")
933
+ if node_type == "public":
934
+ click.echo(f"Neuronum Node '{nodeID}' updated! Visit: {node_url}")
935
+ else:
936
+ click.echo(f"Neuronum Node '{nodeID}' updated!")
833
937
 
834
938
 
835
939
  @click.command()
@@ -955,25 +1059,68 @@ async def async_load(ctx, label):
955
1059
  synapse=env_data.get("SYNAPSE", "")
956
1060
  )
957
1061
 
958
- print(label, ctx)
959
1062
  data = await cell.load(label, ctx)
960
1063
  click.echo(data)
961
1064
 
962
1065
 
1066
+ @click.command()
1067
+ @click.option('--stx', default=None, help="Stream ID (optional)")
1068
+ def sync(stx):
1069
+ asyncio.run(async_sync(stx))
1070
+
1071
+
1072
+ async def async_sync(stx):
1073
+ credentials_folder_path = Path.home() / ".neuronum"
1074
+ env_path = credentials_folder_path / ".env"
1075
+ env_data = {}
1076
+
1077
+ try:
1078
+ with open(env_path, "r") as f:
1079
+ for line in f:
1080
+ key, value = line.strip().split("=")
1081
+ env_data[key] = value
1082
+ except FileNotFoundError:
1083
+ click.echo("No cell connected. Try: neuronum connect-cell")
1084
+ return
1085
+ except Exception as e:
1086
+ click.echo(f"Error reading .env: {e}")
1087
+ return
1088
+
1089
+ cell = neuronum.Cell(
1090
+ host=env_data.get("HOST", ""),
1091
+ password=env_data.get("PASSWORD", ""),
1092
+ network=env_data.get("NETWORK", ""),
1093
+ synapse=env_data.get("SYNAPSE", "")
1094
+ )
1095
+
1096
+ if stx:
1097
+ print(f"Listening to Stream '{stx}'! Close connection with CTRL+C")
1098
+ else:
1099
+ print(f"Listening to '{cell.host}' private Stream! Close connection with CTRL+C")
1100
+ async for operation in cell.sync() if stx is None else cell.sync(stx):
1101
+ label = operation.get("label")
1102
+ data = operation.get("data")
1103
+ ts = operation.get("time")
1104
+ stxID = operation.get("stxID")
1105
+ operator = operation.get("operator")
1106
+ txID = operation.get("txID")
1107
+ print(label, data, ts, operator, txID, stxID)
1108
+
1109
+
963
1110
  cli.add_command(create_cell)
964
- cli.add_command(connect_cell)
965
1111
  cli.add_command(view_cell)
966
1112
  cli.add_command(disconnect_cell)
967
1113
  cli.add_command(delete_cell)
968
1114
  cli.add_command(init_node)
969
1115
  cli.add_command(start_node)
1116
+ cli.add_command(restart_node)
970
1117
  cli.add_command(stop_node)
971
- cli.add_command(connect_node)
1118
+ cli.add_command(check_node)
972
1119
  cli.add_command(update_node)
973
- cli.add_command(disconnect_node)
974
1120
  cli.add_command(delete_node)
975
1121
  cli.add_command(activate)
976
1122
  cli.add_command(load)
1123
+ cli.add_command(sync)
977
1124
 
978
1125
 
979
1126
  if __name__ == "__main__":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: neuronum
3
- Version: 5.6.0
3
+ Version: 5.8.0
4
4
  Summary: Official client library to interact with the Neuronum Network
5
5
  Home-page: https://neuronum.net
6
6
  Author: Neuronum Cybernetics
@@ -18,6 +18,7 @@ Requires-Dist: click
18
18
  Requires-Dist: questionary
19
19
  Requires-Dist: python-dotenv
20
20
  Requires-Dist: requests
21
+ Requires-Dist: psutil
21
22
  Dynamic: author
22
23
  Dynamic: author-email
23
24
  Dynamic: classifier
@@ -51,12 +52,20 @@ Dynamic: summary
51
52
  </a>
52
53
  </p>
53
54
 
54
- ---
55
+ ------------------
56
+
57
+ ### **A Getting Started into the Neuronum Network**
58
+ In this brief getting started guide, you will:
59
+ - [Learn about Neuronum](#about-neuronum)
60
+ - [Connect to the Network](#connect-to-neuronum)
61
+ - [Build a Neuronum Node](#build-on-neuronum)
62
+ - [Interact with your Node](#interact-with-neuronum)
63
+
64
+ ------------------
55
65
 
56
66
  ### **About Neuronum**
57
67
  Neuronum empowers developers to build & connect apps, services, and devices into serverless networks able to exchange data in real time
58
68
 
59
-
60
69
  ### **Features**
61
70
  **Cell & Nodes**
62
71
  - Cell: Account to connect and interact with Neuronum. [Learn More](https://github.com/neuronumcybernetics/neuronum/tree/main/features/cell)
@@ -94,10 +103,13 @@ neuronum connect-cell # connect Cell
94
103
  ------------------
95
104
 
96
105
 
97
- ### **Build On Neuronum** **[(Build with Node Examples)](https://github.com/neuronumcybernetics/neuronum/tree/main/features/nodes)**
106
+ ### **Build On Neuronum**
107
+ [View Node Examples](https://github.com/neuronumcybernetics/neuronum/tree/main/features/nodes)
108
+
109
+
98
110
  Initialize a Node (app template):
99
111
  ```sh
100
- neuronum init-node --app # initialize a Node with app template -> creates a folder named node_<node_id> containing all relevant files
112
+ neuronum init-node --app # initialize a Node with app template
101
113
  ```
102
114
 
103
115
  Change into Node folder
@@ -0,0 +1,10 @@
1
+ cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ cli/main.py,sha256=33d7kyfPeSNJD_lHP5CuFQ34sKM7Mv-kfQIGAbu2DQk,35181
3
+ neuronum/__init__.py,sha256=Drsm263_w3_VWgl1YsKLUr8WwVodqV3TSjqpxLjyq_M,46
4
+ neuronum/neuronum.py,sha256=L8Oz1qcTyOiYMTGfiSKgN5Nu9jcl25jFeJOh0XItPjw,17201
5
+ neuronum-5.8.0.dist-info/licenses/LICENSE.md,sha256=zGst0rjgnp6oFuRVwFwB1Ql4sDXt_nw9xbUR49Gf99A,2008
6
+ neuronum-5.8.0.dist-info/METADATA,sha256=oSNPrPnllCkMzCGcMJjN6cYbcFONcKj2f6fExlfH2AQ,5374
7
+ neuronum-5.8.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ neuronum-5.8.0.dist-info/entry_points.txt,sha256=XKYBcRNxGeJpZZkDPsa8HA_RaJ7Km_R_JaUq5T9Nk2U,42
9
+ neuronum-5.8.0.dist-info/top_level.txt,sha256=ru8Fr84cHm6oHr_DcJ8-uaq3RTiuCRFIr6AC8V0zPu4,13
10
+ neuronum-5.8.0.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- cli/main.py,sha256=WIU5suB-SFPzLswA0i6UyDuNhdevl3vWRAbuh0v4QQY,29888
3
- neuronum/__init__.py,sha256=Drsm263_w3_VWgl1YsKLUr8WwVodqV3TSjqpxLjyq_M,46
4
- neuronum/neuronum.py,sha256=L8Oz1qcTyOiYMTGfiSKgN5Nu9jcl25jFeJOh0XItPjw,17201
5
- neuronum-5.6.0.dist-info/licenses/LICENSE.md,sha256=zGst0rjgnp6oFuRVwFwB1Ql4sDXt_nw9xbUR49Gf99A,2008
6
- neuronum-5.6.0.dist-info/METADATA,sha256=OyxgpKzGQNooCDdXYmOSb58cS18w-Zo3axD9WgPjSwQ,5096
7
- neuronum-5.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
- neuronum-5.6.0.dist-info/entry_points.txt,sha256=XKYBcRNxGeJpZZkDPsa8HA_RaJ7Km_R_JaUq5T9Nk2U,42
9
- neuronum-5.6.0.dist-info/top_level.txt,sha256=ru8Fr84cHm6oHr_DcJ8-uaq3RTiuCRFIr6AC8V0zPu4,13
10
- neuronum-5.6.0.dist-info/RECORD,,