ephys-link 2.0.0b1__tar.gz → 2.0.0b2__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.
Files changed (35) hide show
  1. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/PKG-INFO +2 -1
  2. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/pyproject.toml +5 -1
  3. ephys_link-2.0.0b2/qodana.yaml +29 -0
  4. ephys_link-2.0.0b2/scripts/logger_test.py +17 -0
  5. ephys_link-2.0.0b2/src/ephys_link/__about__.py +1 -0
  6. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/back_end/platform_handler.py +3 -3
  7. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/back_end/server.py +6 -4
  8. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/bindings/ump_4_bindings.py +0 -2
  9. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/util/common.py +2 -3
  10. ephys_link-2.0.0b2/src/ephys_link/util/console.py +133 -0
  11. ephys_link-2.0.0b1/src/ephys_link/__about__.py +0 -1
  12. ephys_link-2.0.0b1/src/ephys_link/util/console.py +0 -112
  13. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/.gitignore +0 -0
  14. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/LICENSE +0 -0
  15. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/README.md +0 -0
  16. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/assets/icon.ico +0 -0
  17. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/ephys_link.spec +0 -0
  18. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/scripts/__init__.py +0 -0
  19. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/scripts/move_tester.py +0 -0
  20. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/scripts/server_tester.py +0 -0
  21. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/__init__.py +0 -0
  22. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/__main__.py +0 -0
  23. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/back_end/__init__.py +0 -0
  24. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/bindings/__init__.py +0 -0
  25. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/bindings/fake_bindings.py +0 -0
  26. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/front_end/__init__.py +0 -0
  27. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/front_end/cli.py +0 -0
  28. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/front_end/gui.py +0 -0
  29. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/resources/CP210xManufacturing.dll +0 -0
  30. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/resources/NstMotorCtrl.dll +0 -0
  31. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/resources/SiUSBXp.dll +0 -0
  32. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/resources/libum.dll +0 -0
  33. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/util/__init__.py +0 -0
  34. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/src/ephys_link/util/base_bindings.py +0 -0
  35. {ephys_link-2.0.0b1 → ephys_link-2.0.0b2}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ephys-link
3
- Version: 2.0.0b1
3
+ Version: 2.0.0b2
4
4
  Summary: A Python Socket.IO server that allows any Socket.IO-compliant application to communicate with manipulators used in electrophysiology experiments.
5
5
  Project-URL: Documentation, https://virtualbrainlab.org/ephys_link/installation_and_use.html
6
6
  Project-URL: Issues, https://github.com/VirtualBrainLab/ephys-link/issues
@@ -33,6 +33,7 @@ Requires-Dist: pyserial==3.5
33
33
  Requires-Dist: python-socketio[asyncio-client]==5.11.3
34
34
  Requires-Dist: pythonnet==3.0.3
35
35
  Requires-Dist: requests==2.32.3
36
+ Requires-Dist: rich==13.7.1
36
37
  Requires-Dist: sensapex==1.400.1
37
38
  Requires-Dist: vbl-aquarium==0.0.19
38
39
  Description-Content-Type: text/markdown
@@ -38,6 +38,7 @@ dependencies = [
38
38
  "pythonnet==3.0.3",
39
39
  "requests==2.32.3",
40
40
  "sensapex==1.400.1",
41
+ "rich==13.7.1",
41
42
  "vbl-aquarium==0.0.19"
42
43
  ]
43
44
 
@@ -114,4 +115,7 @@ exclude_lines = [
114
115
  "no cov",
115
116
  "if __name__ == .__main__.:",
116
117
  "if TYPE_CHECKING:",
117
- ]
118
+ ]
119
+
120
+ [tool.ruff.lint]
121
+ extend-ignore = ["DTZ005"]
@@ -0,0 +1,29 @@
1
+ #-------------------------------------------------------------------------------#
2
+ # Qodana analysis is configured by qodana.yaml file #
3
+ # https://www.jetbrains.com/help/qodana/qodana-yaml.html #
4
+ #-------------------------------------------------------------------------------#
5
+ version: "1.0"
6
+
7
+ #Specify inspection profile for code analysis
8
+ profile:
9
+ name: qodana.starter
10
+
11
+ #Enable inspections
12
+ #include:
13
+ # - name: <SomeEnabledInspectionId>
14
+
15
+ #Disable inspections
16
+ #exclude:
17
+ # - name: <SomeDisabledInspectionId>
18
+ # paths:
19
+ # - <path/where/not/run/inspection>
20
+
21
+ #Execute shell command before Qodana execution (Applied in CI/CD pipeline)
22
+ #bootstrap: sh ./prepare-qodana.sh
23
+
24
+ #Install IDE plugins before Qodana execution (Applied in CI/CD pipeline)
25
+ #plugins:
26
+ # - id: <plugin.id> #(plugin id can be found at https://plugins.jetbrains.com)
27
+
28
+ #Specify Qodana linter for analysis (Applied in CI/CD pipeline)
29
+ linter: jetbrains/qodana-python:latest
@@ -0,0 +1,17 @@
1
+ import logging
2
+
3
+ from rich.logging import RichHandler
4
+
5
+ logging.basicConfig(level="NOTSET", format="%(message)s", datefmt="[%X]", handlers=[RichHandler(rich_tracebacks=True)])
6
+
7
+ log = logging.getLogger("rich")
8
+ log.debug("This message should go to the log file")
9
+ log.info("So should this")
10
+ log.warning("And this, too")
11
+ log.error("And non-ASCII stuff, too, like Øresund and Malmö")
12
+ log.error("[bold red blink]Server is shutting down!", extra={"markup": True})
13
+ log.critical("Critical error! [b red]Server is shutting down!", extra={"markup": True})
14
+ try:
15
+ print(1 / 0)
16
+ except Exception:
17
+ log.exception("[b magenta]unable print![/] [i magenta]asdf", extra={"markup": True})
@@ -0,0 +1 @@
1
+ __version__ = "2.0.0b2"
@@ -71,7 +71,7 @@ class PlatformHandler:
71
71
  return FakeBindings()
72
72
  case _:
73
73
  error_message = f'Platform type "{platform_type}" not recognized.'
74
- self._console.labeled_error_print("PLATFORM", error_message)
74
+ self._console.critical_print(error_message)
75
75
  raise ValueError(error_message)
76
76
 
77
77
  # Ephys Link metadata.
@@ -185,7 +185,7 @@ class PlatformHandler:
185
185
  # Disallow setting manipulator position while inside the brain.
186
186
  if request.manipulator_id in self._inside_brain:
187
187
  error_message = 'Can not move manipulator while inside the brain. Set the depth ("set_depth") instead.'
188
- self._console.error_print(error_message)
188
+ self._console.error_print("Set Position", error_message)
189
189
  return PositionalResponse(error=error_message)
190
190
 
191
191
  # Move to the new position.
@@ -209,7 +209,7 @@ class PlatformHandler:
209
209
  f" position on axis {list(Vector4.model_fields.keys())[index]}."
210
210
  f"Requested: {request.position}, got: {final_unified_position}."
211
211
  )
212
- self._console.error_print(error_message)
212
+ self._console.error_print("Set Position", error_message)
213
213
  return PositionalResponse(error=error_message)
214
214
  except Exception as e:
215
215
  self._console.exception_error_print("Set Position", e)
@@ -75,7 +75,7 @@ class Server:
75
75
  # Helper functions.
76
76
  def _malformed_request_response(self, request: str, data: tuple[tuple[Any], ...]) -> str:
77
77
  """Return a response for a malformed request."""
78
- self._console.labeled_error_print("MALFORMED REQUEST", f"{request}: {data}")
78
+ self._console.error_print("MALFORMED REQUEST", f"{request}: {data}")
79
79
  return dumps({"error": "Malformed request."})
80
80
 
81
81
  async def _run_if_data_available(
@@ -127,7 +127,9 @@ class Server:
127
127
  self._console.info_print("CONNECTION GRANTED", sid)
128
128
  return True
129
129
 
130
- self._console.error_print(f"CONNECTION REFUSED to {sid}. Client {self._client_sid} already connected.")
130
+ self._console.error_print(
131
+ "CONNECTION REFUSED", f"Cannot connect {sid} as {self._client_sid} is already connected."
132
+ )
131
133
  return False
132
134
 
133
135
  async def disconnect(self, sid: str) -> None:
@@ -142,7 +144,7 @@ class Server:
142
144
  if self._client_sid == sid:
143
145
  self._client_sid = ""
144
146
  else:
145
- self._console.error_print(f"Client {sid} disconnected without being connected.")
147
+ self._console.error_print("DISCONNECTION", f"Client {sid} disconnected without being connected.")
146
148
 
147
149
  # noinspection PyTypeChecker
148
150
  async def platform_event_handler(self, event: str, *args: tuple[Any]) -> str:
@@ -196,5 +198,5 @@ class Server:
196
198
  case "stop_all":
197
199
  return await self._platform_handler.stop_all()
198
200
  case _:
199
- self._console.error_print(f"Unknown event: {event}.")
201
+ self._console.error_print("EVENT", f"Unknown event: {event}.")
200
202
  return dumps({"error": "Unknown event."})
@@ -10,7 +10,6 @@ from vbl_aquarium.models.unity import Vector3, Vector4
10
10
 
11
11
  from ephys_link.util.base_bindings import BaseBindings
12
12
  from ephys_link.util.common import RESOURCES_PATH, array_to_vector4, mm_to_um, mmps_to_umps, um_to_mm, vector4_to_array
13
- from ephys_link.util.console import Console
14
13
 
15
14
 
16
15
  class Ump4Bindings(BaseBindings):
@@ -24,7 +23,6 @@ class Ump4Bindings(BaseBindings):
24
23
  self._ump = UMP.get_ump()
25
24
  if self._ump is None:
26
25
  error_message = "Unable to connect to uMp"
27
- Console.error_print(error_message)
28
26
  raise ValueError(error_message)
29
27
 
30
28
  async def get_manipulators(self) -> list[str]:
@@ -9,7 +9,6 @@ from requests import get
9
9
  from vbl_aquarium.models.unity import Vector4
10
10
 
11
11
  from ephys_link.__about__ import __version__
12
- from ephys_link.util.console import Console
13
12
 
14
13
  # Ephys Link ASCII.
15
14
  ASCII = r"""
@@ -47,8 +46,8 @@ def check_for_updates() -> None:
47
46
  response = get("https://api.github.com/repos/VirtualBrainLab/ephys-link/tags", timeout=10)
48
47
  latest_version = response.json()[0]["name"]
49
48
  if parse(latest_version) > parse(__version__):
50
- Console.info_print("Update available", latest_version)
51
- Console.info_print("", "Download at: https://github.com/VirtualBrainLab/ephys-link/releases/latest")
49
+ print(f"Update available: {latest_version} !")
50
+ print("Download at: https://github.com/VirtualBrainLab/ephys-link/releases/latest")
52
51
 
53
52
 
54
53
  # Unit conversions
@@ -0,0 +1,133 @@
1
+ # ruff: noqa: T201
2
+ """Console class for printing messages to the console.
3
+
4
+ Configure the console to print error and debug messages.
5
+
6
+ Usage: Create a Console object and call the appropriate method to print messages.
7
+ """
8
+
9
+ from logging import DEBUG, ERROR, INFO, basicConfig, getLogger
10
+
11
+ from rich.logging import RichHandler
12
+ from rich.traceback import install
13
+
14
+
15
+ class Console:
16
+ def __init__(self, *, enable_debug: bool) -> None:
17
+ """Initialize console properties.
18
+
19
+ :param enable_debug: Enable debug mode.
20
+ :type enable_debug: bool
21
+ """
22
+ # Repeat message fields.
23
+ self._last_message = (0, "", "")
24
+ self._repeat_counter = 0
25
+
26
+ # Config logger.
27
+ basicConfig(
28
+ level=DEBUG if enable_debug else INFO,
29
+ format="%(message)s",
30
+ datefmt="[%I:%M:%S %p]",
31
+ handlers=[RichHandler(rich_tracebacks=True)],
32
+ )
33
+ self._log = getLogger("rich")
34
+
35
+ # Install Rich traceback.
36
+ install()
37
+
38
+ def debug_print(self, label: str, msg: str) -> None:
39
+ """Print a debug message to the console.
40
+
41
+ :param label: Label for the debug message.
42
+ :type label: str
43
+ :param msg: Debug message to print.
44
+ :type msg: str
45
+ """
46
+ self._repeatable_log(DEBUG, f"[b green]{label}", f"[green]{msg}")
47
+
48
+ def info_print(self, label: str, msg: str) -> None:
49
+ """Print info to console.
50
+
51
+ :param label: Label for the message.
52
+ :type label: str
53
+ :param msg: Message to print.
54
+ :type msg: str
55
+ """
56
+ self._repeatable_log(INFO, f"[b blue]{label}", msg)
57
+
58
+ def error_print(self, label: str, msg: str) -> None:
59
+ """Print an error message to the console.
60
+
61
+ :param label: Label for the error message.
62
+ :type label: str
63
+ :param msg: Error message to print.
64
+ :type msg: str
65
+ """
66
+ self._repeatable_log(ERROR, f"[b red]{label}", f"[red]{msg}")
67
+
68
+ def critical_print(self, msg: str) -> None:
69
+ """Print a critical message to the console.
70
+
71
+ :param msg: Critical message to print.
72
+ :type msg: str
73
+ """
74
+ self._log.critical(f"[b i red]{msg}", extra={"markup": True})
75
+
76
+ @staticmethod
77
+ def pretty_exception(exception: Exception) -> str:
78
+ """Pretty print an exception.
79
+
80
+ :param exception: Exception to pretty print.
81
+ :type exception: Exception
82
+ :return: Pretty printed exception.
83
+ :rtype: str
84
+ """
85
+ return f"{type(exception).__name__}: {exception}"
86
+
87
+ def exception_error_print(self, label: str, exception: Exception) -> None:
88
+ """Print an error message with exception details to the console.
89
+
90
+ :param label: Label for the error message.
91
+ :type label: str
92
+ :param exception: Exception to print.
93
+ :type exception: Exception
94
+ """
95
+ self._log.exception(
96
+ f"[b magenta]{label}:[/] [magenta]{Console.pretty_exception(exception)}", extra={"markup": True}
97
+ )
98
+
99
+ # Helper methods.
100
+ def _repeatable_log(self, log_type: int, label: str, message: str) -> None:
101
+ """Add a row to the output table.
102
+
103
+ :param log_type: Type of log.
104
+ :type log_type: int
105
+ :param label: Label for the message.
106
+ :type label: str
107
+ :param message: Message.
108
+ :type message: str
109
+ """
110
+
111
+ # Compute if this is a repeated message.
112
+ message_set = (log_type, label, message)
113
+ if message_set == self._last_message:
114
+ # Handle repeat.
115
+ self._repeat_counter += 1
116
+
117
+ # Add an ellipsis row for first repeat.
118
+ if self._repeat_counter == 1:
119
+ self._log.log(log_type, "...")
120
+ else:
121
+ # Handle novel message.
122
+ if self._repeat_counter > 0:
123
+ # Complete previous repeat.
124
+ self._log.log(
125
+ self._last_message[0],
126
+ f"{self._last_message[1]}:[/] {self._last_message[2]}[/] x {self._repeat_counter}",
127
+ extra={"markup": True},
128
+ )
129
+ self._repeat_counter = 0
130
+
131
+ # Log new message.
132
+ self._log.log(log_type, f"{label}:[/] {message}", extra={"markup": True})
133
+ self._last_message = message_set
@@ -1 +0,0 @@
1
- __version__ = "2.0.0b1"
@@ -1,112 +0,0 @@
1
- # ruff: noqa: T201
2
- """Console class for printing messages to the console.
3
-
4
- Configure the console to print error and debug messages.
5
-
6
- Usage: Create a Console object and call the appropriate method to print messages.
7
- """
8
-
9
- from traceback import print_exc
10
-
11
- from colorama import Back, Fore, Style, init
12
-
13
- # Constants.
14
- TAB_BLOCK = "\t\t"
15
-
16
-
17
- class Console:
18
- def __init__(self, *, enable_debug: bool) -> None:
19
- """Initialize console properties.
20
-
21
- :param enable_debug: Enable debug mode.
22
- :type enable_debug: bool
23
- """
24
- self._enable_debug = enable_debug
25
-
26
- # Repeat message fields.
27
- self._last_message = ""
28
- self._repeat_counter = 1
29
-
30
- # Initialize colorama.
31
- init(autoreset=True)
32
-
33
- @staticmethod
34
- def error_print(msg: str) -> None:
35
- """Print an error message to the console.
36
-
37
- :param msg: Error message to print.
38
- :type msg: str
39
- """
40
- print(f"\n{Back.RED}{Style.BRIGHT} ERROR {Style.RESET_ALL}{TAB_BLOCK}{Fore.RED}{msg}")
41
-
42
- @staticmethod
43
- def labeled_error_print(label: str, msg: str) -> None:
44
- """Print an error message with a label to the console.
45
-
46
- :param label: Label for the error message.
47
- :type label: str
48
- :param msg: Error message to print.
49
- :type msg: str
50
- """
51
- print(f"\n{Back.RED}{Style.BRIGHT} ERROR {label} {Style.RESET_ALL}{TAB_BLOCK}{Fore.RED}{msg}")
52
-
53
- @staticmethod
54
- def pretty_exception(exception: Exception) -> str:
55
- """Pretty print an exception.
56
-
57
- :param exception: Exception to pretty print.
58
- :type exception: Exception
59
- :return: Pretty printed exception.
60
- :rtype: str
61
- """
62
- return f"{type(exception).__name__}: {exception}"
63
-
64
- @staticmethod
65
- def exception_error_print(label: str, exception: Exception) -> None:
66
- """Print an error message with exception details to the console.
67
-
68
- :param label: Label for the error message.
69
- :type label: str
70
- :param exception: Exception to print.
71
- :type exception: Exception
72
- """
73
- Console.labeled_error_print(label, Console.pretty_exception(exception))
74
- print_exc()
75
-
76
- def debug_print(self, label: str, msg: str) -> None:
77
- """Print a debug message to the console.
78
-
79
- :param label: Label for the debug message.
80
- :type label: str
81
- :param msg: Debug message to print.
82
- :type msg: str
83
- """
84
- if self._enable_debug:
85
- self._repeat_print(f"{Back.BLUE}{Style.BRIGHT} DEBUG {label} {Style.RESET_ALL}{TAB_BLOCK}{Fore.BLUE}{msg}")
86
-
87
- @staticmethod
88
- def info_print(label: str, msg: str) -> None:
89
- """Print info to console.
90
-
91
- :param label: Label for the message.
92
- :type label: str
93
- :param msg: Message to print.
94
- :type msg: str
95
- """
96
- print(f"\n{Back.GREEN}{Style.BRIGHT} {label} {Style.RESET_ALL}{TAB_BLOCK}{Fore.GREEN}{msg}")
97
-
98
- # Helper methods.
99
- def _repeat_print(self, msg: str) -> None:
100
- """Print a message to the console with repeat counter.
101
-
102
- :param msg: Message to print.
103
- :type msg: str
104
- """
105
- if msg == self._last_message:
106
- self._repeat_counter += 1
107
- else:
108
- self._repeat_counter = 1
109
- self._last_message = msg
110
- print()
111
-
112
- print(f"\r{msg}{f" (x{self._repeat_counter})" if self._repeat_counter > 1 else ""}", end="")
File without changes
File without changes
File without changes