iris-pex-embedded-python 3.4.1b9__tar.gz → 3.4.1b10__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.

Potentially problematic release.


This version of iris-pex-embedded-python might be problematic. Click here for more details.

Files changed (87) hide show
  1. {iris_pex_embedded_python-3.4.1b9/src/iris_pex_embedded_python.egg-info → iris_pex_embedded_python-3.4.1b10}/PKG-INFO +1 -1
  2. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/pyproject.toml +1 -1
  3. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_business_operation.py +0 -1
  4. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_business_process.py +0 -1
  5. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_business_service.py +0 -3
  6. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_common.py +7 -70
  7. iris_pex_embedded_python-3.4.1b10/src/iop/_debugpy.py +145 -0
  8. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/Common.cls +3 -1
  9. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10/src/iris_pex_embedded_python.egg-info}/PKG-INFO +1 -1
  10. iris_pex_embedded_python-3.4.1b9/src/iop/_debugpy.py +0 -69
  11. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/LICENSE +0 -0
  12. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/README.md +0 -0
  13. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/setup.cfg +0 -0
  14. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/setup.py +0 -0
  15. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/__init__.py +0 -0
  16. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/BusinessOperation.cls +0 -0
  17. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/BusinessProcess.cls +0 -0
  18. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/BusinessService.cls +0 -0
  19. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/Common.cls +0 -0
  20. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/Director.cls +0 -0
  21. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/Duplex/Operation.cls +0 -0
  22. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/Duplex/Process.cls +0 -0
  23. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/Duplex/Service.cls +0 -0
  24. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/InboundAdapter.cls +0 -0
  25. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/Message.cls +0 -0
  26. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/OutboundAdapter.cls +0 -0
  27. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/PickleMessage.cls +0 -0
  28. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +0 -0
  29. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +0 -0
  30. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +0 -0
  31. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +0 -0
  32. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +0 -0
  33. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/Test.cls +0 -0
  34. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/PEX/Utils.cls +0 -0
  35. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/cls/Grongier/Service/WSGI.cls +0 -0
  36. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/pex/__init__.py +0 -0
  37. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/pex/__main__.py +0 -0
  38. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/pex/_business_host.py +0 -0
  39. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/pex/_cli.py +0 -0
  40. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/pex/_common.py +0 -0
  41. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/pex/_director.py +0 -0
  42. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/pex/_utils.py +0 -0
  43. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/grongier/pex/wsgi/handlers.py +0 -0
  44. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/__init__.py +0 -0
  45. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/__main__.py +0 -0
  46. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_async_request.py +0 -0
  47. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_business_host.py +0 -0
  48. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_cli.py +0 -0
  49. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_decorators.py +0 -0
  50. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_director.py +0 -0
  51. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_dispatch.py +0 -0
  52. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_inbound_adapter.py +0 -0
  53. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_iris.py +0 -0
  54. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_log_manager.py +0 -0
  55. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_message.py +0 -0
  56. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_message_validator.py +0 -0
  57. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_outbound_adapter.py +0 -0
  58. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_private_session_duplex.py +0 -0
  59. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_private_session_process.py +0 -0
  60. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_serialization.py +0 -0
  61. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/_utils.py +0 -0
  62. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/BusinessOperation.cls +0 -0
  63. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/BusinessProcess.cls +0 -0
  64. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/BusinessService.cls +0 -0
  65. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/Director.cls +0 -0
  66. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/Duplex/Operation.cls +0 -0
  67. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/Duplex/Process.cls +0 -0
  68. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/Duplex/Service.cls +0 -0
  69. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/InboundAdapter.cls +0 -0
  70. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/Message/JSONSchema.cls +0 -0
  71. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/Message.cls +0 -0
  72. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/OutboundAdapter.cls +0 -0
  73. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/PickleMessage.cls +0 -0
  74. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/PrivateSession/Duplex.cls +0 -0
  75. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/PrivateSession/Message/Ack.cls +0 -0
  76. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/PrivateSession/Message/Poll.cls +0 -0
  77. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/PrivateSession/Message/Start.cls +0 -0
  78. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/PrivateSession/Message/Stop.cls +0 -0
  79. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/Service/WSGI.cls +0 -0
  80. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/Test.cls +0 -0
  81. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/cls/IOP/Utils.cls +0 -0
  82. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iop/wsgi/handlers.py +0 -0
  83. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iris_pex_embedded_python.egg-info/SOURCES.txt +0 -0
  84. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iris_pex_embedded_python.egg-info/dependency_links.txt +0 -0
  85. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iris_pex_embedded_python.egg-info/entry_points.txt +0 -0
  86. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iris_pex_embedded_python.egg-info/requires.txt +0 -0
  87. {iris_pex_embedded_python-3.4.1b9 → iris_pex_embedded_python-3.4.1b10}/src/iris_pex_embedded_python.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iris_pex_embedded_python
3
- Version: 3.4.1b9
3
+ Version: 3.4.1b10
4
4
  Summary: Iris Interoperability based on Embedded Python
5
5
  Author-email: grongier <guillaume.rongier@intersystems.com>
6
6
  License: MIT License
@@ -3,7 +3,7 @@ requires = ["setuptools", "wheel"]
3
3
 
4
4
  [project]
5
5
  name = "iris_pex_embedded_python"
6
- version = "3.4.1b9"
6
+ version = "3.4.1b10"
7
7
  description = "Iris Interoperability based on Embedded Python"
8
8
  readme = "README.md"
9
9
  authors = [
@@ -47,7 +47,6 @@ class _BusinessOperation(_BusinessHost):
47
47
 
48
48
  def _dispatch_on_init(self, host_object: Any) -> None:
49
49
  """For internal use only."""
50
- self._debugpy(host_object=host_object)
51
50
  create_dispatch(self)
52
51
  self.on_init()
53
52
  return
@@ -141,7 +141,6 @@ class _BusinessProcess(_BusinessHost):
141
141
  def _dispatch_on_init(self, host_object: Any) -> None:
142
142
  """For internal use only."""
143
143
  self._restore_persistent_properties(host_object)
144
- self._debugpy(host_object=host_object)
145
144
  create_dispatch(self)
146
145
  self.on_init()
147
146
  self._save_persistent_properties(host_object)
@@ -18,9 +18,6 @@ class _BusinessService(_BusinessHost):
18
18
 
19
19
  def _dispatch_on_init(self, host_object) -> None:
20
20
  """For internal use only."""
21
-
22
- self._debugpy(host_object=host_object)
23
-
24
21
  self.on_init()
25
22
 
26
23
  return
@@ -8,6 +8,8 @@ from . import _iris
8
8
 
9
9
  from iop._log_manager import LogManager, logging
10
10
 
11
+ from iop._debugpy import debugpython
12
+
11
13
  class _Common(metaclass=abc.ABCMeta):
12
14
  """Base class that defines common methods for all component types.
13
15
 
@@ -59,78 +61,8 @@ class _Common(metaclass=abc.ABCMeta):
59
61
 
60
62
  def _dispatch_on_init(self, host_object: Any) -> None:
61
63
  """Initialize component when started."""
62
- self._debugpy(host_object)
63
64
  self.on_init()
64
65
 
65
- def _debugpy(self, host_object: Any) -> None:
66
- """Enable debugpy for debugging purposes."""
67
- # iris.cls('%SYS.Python').Debugging(1)
68
- from iop._debugpy import enable_debugpy, wait_for_debugpy_connected, find_free_port, is_debugpy_installed
69
- import sys, os
70
-
71
- # hack to set __file__ for os module in debugpy
72
- # This is a workaround for the issue where debugpy cannot find the __file__ attribute of the os module.
73
- if not hasattr(os, '__file__'):
74
- setattr(os, '__file__', __file__)
75
-
76
- if host_object is not None:
77
- port = host_object.port
78
- timeout = host_object.timeout
79
- enabled = host_object.enable
80
- python_interpreter_path = host_object.PythonInterpreterPath
81
- else:
82
- self.log_alert("No host object found, cannot enable debugpy.")
83
- return
84
-
85
- if python_interpreter_path != "":
86
- # the user has set a specific python interpreter path, so we need to set the path to the python executable
87
- # to the one that is running this script
88
- if os.path.exists(python_interpreter_path):
89
- sys.executable = python_interpreter_path
90
- else:
91
- self.log_alert(f"Python path {python_interpreter_path} does not exist, cannot set python path for debugpy.")
92
- return
93
- elif sys.executable.find('irisdb') > 0:
94
- # the executable is the IRIS executable, so we need to set the path to the python executable
95
- # to the one that is running this script
96
- installdir = os.environ.get('IRISINSTALLDIR') or os.environ.get('ISC_PACKAGE_INSTALLDIR')
97
- if installdir is not None:
98
- if sys.platform == 'win32':
99
- python_path = os.path.join(installdir, 'bin', 'irispython.exe')
100
- else:
101
- python_path = os.path.join(installdir, 'bin', 'irispython')
102
- if os.path.exists(python_path):
103
- sys.executable = python_path
104
- else:
105
- self.log_alert(f"Python path {python_path} does not exist, cannot set python path for debugpy.")
106
- return
107
- else:
108
- self.log_alert("IRISINSTALLDIR or ISC_PACKAGE_INSTALLDIR not set, cannot set python path for debugpy.")
109
- return
110
-
111
- if enabled:
112
- self.log_info(f"Debugpy is running in {sys.executable}.")
113
- if is_debugpy_installed():
114
- if port is None or port <= 0:
115
- port = find_free_port()
116
- self.log_info(f"Debugpy enabled.")
117
- try:
118
- enable_debugpy(port=port, address=None)
119
- except Exception as e:
120
- self.log_alert(f"Error enabling debugpy: {e}")
121
- return
122
-
123
- self.trace(f"Waiting for {timeout} sec to debugpy connection on port {port}...")
124
- if wait_for_debugpy_connected(timeout=timeout, port=port):
125
- self.log_info("Debugpy connected.")
126
- else:
127
- self.log_alert(f"Debugpy connection timed out after {timeout} seconds.")
128
- else:
129
- self.log_alert("Debugpy is not installed.")
130
- else:
131
- self.log_info("Debugpy is not enabled.")
132
-
133
-
134
66
  def _dispatch_on_tear_down(self, host_object: Any) -> None:
135
67
  self.on_tear_down()
136
68
 
@@ -138,6 +70,11 @@ class _Common(metaclass=abc.ABCMeta):
138
70
  """Internal method to set IRIS handles."""
139
71
  pass
140
72
 
73
+ def _debugpy(self, host) -> None:
74
+ """Set up debugpy for debugging."""
75
+ if debugpython is not None:
76
+ debugpython(self=self, host_object=host)
77
+
141
78
  # Component information methods
142
79
  @classmethod
143
80
  def _get_info(cls) -> List[str]:
@@ -0,0 +1,145 @@
1
+
2
+ import threading
3
+ import time
4
+ import contextlib
5
+ import socket
6
+ import os
7
+ import sys
8
+ from contextlib import closing
9
+ from typing import Optional, cast, Any, Dict
10
+
11
+ def find_free_port(start: Optional[int] = None, end: Optional[int] = None) -> int:
12
+ port = start
13
+ if port is None:
14
+ port = 0
15
+ if end is None:
16
+ end = port
17
+
18
+ try:
19
+ with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
20
+ with contextlib.suppress(Exception):
21
+ s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
22
+
23
+ s.bind(("0.0.0.0", port))
24
+
25
+ s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
26
+ return cast(int, s.getsockname()[1])
27
+ except (SystemExit, KeyboardInterrupt):
28
+ raise
29
+ except BaseException:
30
+ if port and end > port:
31
+ return find_free_port(port + 1, end)
32
+ if start and start > 0:
33
+ return find_free_port(None)
34
+ raise
35
+
36
+ def is_debugpy_installed() -> bool:
37
+ try:
38
+ __import__("debugpy")
39
+ return True
40
+ except ImportError:
41
+ return False
42
+
43
+ def _get_python_interpreter_path(install_dir: Optional[str]) -> Optional[str]:
44
+ """Get the path to the Python interpreter."""
45
+ if not install_dir:
46
+ return None
47
+
48
+ python_exe = 'irispython.exe' if sys.platform == 'win32' else 'irispython'
49
+ python_path = os.path.join(install_dir, 'bin', python_exe)
50
+
51
+ return python_path if os.path.exists(python_path) else None
52
+
53
+ def _get_debugpy_config(python_path: str) -> Dict[str, str]:
54
+ """Get the debugpy configuration."""
55
+ return {"python": python_path}
56
+
57
+ def configure_debugpy(self, python_path: Optional[str] = None) -> bool:
58
+ """Configure debugpy with the appropriate Python interpreter."""
59
+ import debugpy
60
+
61
+ if not python_path:
62
+ install_dir = os.environ.get('IRISINSTALLDIR') or os.environ.get('ISC_PACKAGE_INSTALLDIR')
63
+ python_path = _get_python_interpreter_path(install_dir)
64
+
65
+ if not python_path:
66
+ self.log_alert("Could not determine Python interpreter path")
67
+ return False
68
+
69
+ try:
70
+ debugpy.configure(_get_debugpy_config(python_path))
71
+ self.log_info(f"Debugpy configured with Python interpreter: {python_path}")
72
+ return True
73
+ except Exception as e:
74
+ self.log_alert(f"Failed to configure debugpy: {e}")
75
+ return False
76
+
77
+ def wait_for_debugpy_connected(timeout: float = 30, port: int = 0) -> bool:
78
+ """Wait for debugpy client to connect."""
79
+ import debugpy
80
+
81
+ if not is_debugpy_installed():
82
+ return False
83
+
84
+ def timeout_handler():
85
+ time.sleep(timeout)
86
+ debugpy.wait_for_client.cancel()
87
+
88
+ threading.Thread(target=timeout_handler, daemon=True).start()
89
+
90
+ try:
91
+ debugpy.wait_for_client()
92
+ return debugpy.is_client_connected()
93
+ except Exception:
94
+ import pydevd # type: ignore
95
+ pydevd.stoptrace()
96
+ return False
97
+
98
+ def enable_debugpy(port: int, address: str = "0.0.0.0") -> None:
99
+ """Enable debugpy server on specified port and address."""
100
+ import debugpy
101
+ debugpy.listen((address, port))
102
+
103
+ def debugpython(self, host_object: Any) -> None:
104
+ """Enable and configure debugpy for debugging purposes."""
105
+ # hack to set __file__ for os module in debugpy
106
+ # This is a workaround for the issue where debugpy cannot find the __file__ attribute of the os module.
107
+ if not hasattr(os, '__file__'):
108
+ setattr(os, '__file__', __file__)
109
+
110
+
111
+ if host_object is None:
112
+ self.log_alert("No host object found, cannot enable debugpy.")
113
+ return
114
+
115
+ if host_object.enable != 1:
116
+ self.log_info("Debugpy is not enabled.")
117
+ return
118
+
119
+ if not is_debugpy_installed():
120
+ self.log_alert("Debugpy is not installed.")
121
+ return
122
+
123
+ # Configure Python interpreter
124
+ if host_object.PythonInterpreterPath != '':
125
+ success = configure_debugpy(self, host_object.PythonInterpreterPath)
126
+ else:
127
+ success = configure_debugpy(self)
128
+
129
+ if not success:
130
+ return
131
+
132
+ # Setup debugging server
133
+ port = host_object.port if host_object.port and host_object.port > 0 else find_free_port()
134
+
135
+ try:
136
+ enable_debugpy(port=port)
137
+ self.log_info(f"Debugpy enabled on port {port}")
138
+
139
+ self.trace(f"Waiting {host_object.timeout} seconds for debugpy connection...")
140
+ if wait_for_debugpy_connected(timeout=host_object.timeout, port=port):
141
+ self.log_info("Debugpy connected successfully")
142
+ else:
143
+ self.log_alert(f"Debugpy connection timed out after {host_object.timeout} seconds")
144
+ except Exception as e:
145
+ self.log_alert(f"Error enabling debugpy: {e}")
@@ -40,7 +40,9 @@ Method GetModule() As %String
40
40
  Method %OnNew(pConfigName As %String) As %Status
41
41
  {
42
42
  $$$ThrowOnError(..Connect())
43
- Quit $method($this,"initConfig",.pConfigName) ; call subclass
43
+ $$$ThrowOnError($method($this,"initConfig",.pConfigName)) ; call subclass
44
+ do ..%class."_debugpy"($this)
45
+ Quit $$$OK
44
46
  }
45
47
 
46
48
  Method OnInit() As %Status
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iris_pex_embedded_python
3
- Version: 3.4.1b9
3
+ Version: 3.4.1b10
4
4
  Summary: Iris Interoperability based on Embedded Python
5
5
  Author-email: grongier <guillaume.rongier@intersystems.com>
6
6
  License: MIT License
@@ -1,69 +0,0 @@
1
-
2
- import threading
3
- import time
4
- import contextlib
5
- import socket
6
- from contextlib import closing
7
-
8
- from typing import Optional, Tuple, Union, Sequence, cast, Callable, TypeVar, Dict, Any
9
-
10
- def find_free_port(start: Optional[int] = None, end: Optional[int] = None) -> int:
11
- port = start
12
- if port is None:
13
- port = 0
14
- if end is None:
15
- end = port
16
-
17
- try:
18
- with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
19
- with contextlib.suppress(Exception):
20
- s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
21
-
22
- s.bind(("0.0.0.0", port))
23
-
24
- s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
25
- return cast(int, s.getsockname()[1])
26
- except (SystemExit, KeyboardInterrupt):
27
- raise
28
- except BaseException:
29
- if port and end > port:
30
- return find_free_port(port + 1, end)
31
- if start and start > 0:
32
- return find_free_port(None)
33
- raise
34
-
35
-
36
- def is_debugpy_installed() -> bool:
37
- try:
38
- __import__("debugpy")
39
- except ImportError:
40
- return False
41
- return True
42
-
43
-
44
- def wait_for_debugpy_connected(timeout: float = 30,port=0) -> bool:
45
- import debugpy # noqa: T100
46
-
47
- if not is_debugpy_installed():
48
- return False
49
-
50
- class T(threading.Thread):
51
- daemon = True
52
- def run(self):
53
- time.sleep(timeout)
54
- debugpy.wait_for_client.cancel()
55
- T().start()
56
- debugpy.wait_for_client()
57
- if debugpy.is_client_connected():
58
- return True
59
-
60
- return False
61
-
62
- def enable_debugpy(port: int, address = None) -> bool:
63
-
64
- import debugpy # noqa: T100
65
-
66
- if address is None:
67
- address = "0.0.0.0"
68
-
69
- debugpy.listen((address, port))