iris-pex-embedded-python 3.5.1b4__tar.gz → 3.5.2__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 (97) hide show
  1. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/PKG-INFO +1 -1
  2. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/pyproject.toml +1 -1
  3. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_cli.py +1 -1
  4. iris_pex_embedded_python-3.5.2/src/iop/_remote.py +91 -0
  5. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_utils.py +157 -17
  6. iris_pex_embedded_python-3.5.2/src/iop/cls/IOP/Projection.cls +49 -0
  7. iris_pex_embedded_python-3.5.2/src/iop/cls/IOP/Service/Remote/Handler.cls +30 -0
  8. iris_pex_embedded_python-3.5.2/src/iop/cls/IOP/Service/Remote/Rest/v1.cls +97 -0
  9. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iris_pex_embedded_python.egg-info/PKG-INFO +1 -1
  10. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iris_pex_embedded_python.egg-info/SOURCES.txt +4 -0
  11. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/LICENSE +0 -0
  12. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/README.md +0 -0
  13. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/setup.cfg +0 -0
  14. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/setup.py +0 -0
  15. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/__init__.py +0 -0
  16. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/BusinessOperation.cls +0 -0
  17. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/BusinessProcess.cls +0 -0
  18. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/BusinessService.cls +0 -0
  19. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/Common.cls +0 -0
  20. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/Director.cls +0 -0
  21. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/Duplex/Operation.cls +0 -0
  22. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/Duplex/Process.cls +0 -0
  23. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/Duplex/Service.cls +0 -0
  24. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/InboundAdapter.cls +0 -0
  25. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/Message.cls +0 -0
  26. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/OutboundAdapter.cls +0 -0
  27. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/PickleMessage.cls +0 -0
  28. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +0 -0
  29. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +0 -0
  30. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +0 -0
  31. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +0 -0
  32. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +0 -0
  33. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/Test.cls +0 -0
  34. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/PEX/Utils.cls +0 -0
  35. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/cls/Grongier/Service/WSGI.cls +0 -0
  36. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/pex/__init__.py +0 -0
  37. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/pex/__main__.py +0 -0
  38. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/pex/_business_host.py +0 -0
  39. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/pex/_cli.py +0 -0
  40. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/pex/_common.py +0 -0
  41. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/pex/_director.py +0 -0
  42. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/pex/_utils.py +0 -0
  43. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/grongier/pex/wsgi/handlers.py +0 -0
  44. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/__init__.py +0 -0
  45. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/__main__.py +0 -0
  46. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_async_request.py +0 -0
  47. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_business_host.py +0 -0
  48. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_business_operation.py +0 -0
  49. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_business_process.py +0 -0
  50. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_business_service.py +0 -0
  51. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_common.py +0 -0
  52. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_debugpy.py +0 -0
  53. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_decorators.py +0 -0
  54. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_director.py +0 -0
  55. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_dispatch.py +0 -0
  56. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_generator_request.py +0 -0
  57. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_inbound_adapter.py +0 -0
  58. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_iris.py +0 -0
  59. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_log_manager.py +0 -0
  60. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_message.py +0 -0
  61. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_message_validator.py +0 -0
  62. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_outbound_adapter.py +0 -0
  63. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_private_session_duplex.py +0 -0
  64. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_private_session_process.py +0 -0
  65. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/_serialization.py +0 -0
  66. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/BusinessOperation.cls +0 -0
  67. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/BusinessProcess.cls +0 -0
  68. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/BusinessService.cls +0 -0
  69. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Common.cls +0 -0
  70. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Director.cls +0 -0
  71. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Duplex/Operation.cls +0 -0
  72. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Duplex/Process.cls +0 -0
  73. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Duplex/Service.cls +0 -0
  74. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Generator/Message/Ack.cls +0 -0
  75. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Generator/Message/Poll.cls +0 -0
  76. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Generator/Message/Start.cls +0 -0
  77. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Generator/Message/StartPickle.cls +0 -0
  78. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Generator/Message/Stop.cls +0 -0
  79. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/InboundAdapter.cls +0 -0
  80. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Message/JSONSchema.cls +0 -0
  81. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Message.cls +0 -0
  82. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/OutboundAdapter.cls +0 -0
  83. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/PickleMessage.cls +0 -0
  84. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/PrivateSession/Duplex.cls +0 -0
  85. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/PrivateSession/Message/Ack.cls +0 -0
  86. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/PrivateSession/Message/Poll.cls +0 -0
  87. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/PrivateSession/Message/Start.cls +0 -0
  88. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/PrivateSession/Message/Stop.cls +0 -0
  89. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Service/WSGI.cls +0 -0
  90. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Test.cls +0 -0
  91. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Utils.cls +0 -0
  92. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/cls/IOP/Wrapper.cls +0 -0
  93. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iop/wsgi/handlers.py +0 -0
  94. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iris_pex_embedded_python.egg-info/dependency_links.txt +0 -0
  95. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iris_pex_embedded_python.egg-info/entry_points.txt +0 -0
  96. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/src/iris_pex_embedded_python.egg-info/requires.txt +0 -0
  97. {iris_pex_embedded_python-3.5.1b4 → iris_pex_embedded_python-3.5.2}/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.5.1b4
3
+ Version: 3.5.2
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.5.1b4"
6
+ version = "3.5.2"
7
7
  description = "Iris Interoperability based on Embedded Python"
8
8
  readme = "README.md"
9
9
  authors = [
@@ -147,7 +147,7 @@ class Command:
147
147
  if migrate_path is not None:
148
148
  if not os.path.isabs(migrate_path):
149
149
  migrate_path = os.path.join(os.getcwd(), migrate_path)
150
- _Utils.migrate(migrate_path)
150
+ _Utils.migrate_remote(migrate_path)
151
151
 
152
152
  def _handle_log(self) -> None:
153
153
  if self.args.log == 'not_set':
@@ -0,0 +1,91 @@
1
+ # This module provides a REST API for remote I/O operations
2
+ # It uses Flask to handle incoming requests and route them to the appropriate I/O functions
3
+ # It should be able to help the migrate command of IoP Cli to work remotely:
4
+ # this means copy all the .py files from the current directory of (settings.py) to the remote server
5
+ # and run the api migrate from the remote server
6
+ # the default folder is based on the NAMESPACE variable in settings.py
7
+ from flask import Flask, request, jsonify
8
+
9
+ @app.route('/remote_io', methods=['POST'])
10
+ def remote_io():
11
+ data = request.json
12
+ if not data or 'operation' not in data:
13
+ return jsonify({'error': 'Invalid request'}), 400
14
+
15
+ operation = data['operation']
16
+ # Here you would implement the logic to handle the operation
17
+ # For example, you could call a function that performs the I/O operation
18
+ # and return the result as JSON.
19
+
20
+ # Placeholder response for demonstration purposes
21
+ response = {'status': 'success', 'operation': operation}
22
+ return jsonify(response), 200
23
+
24
+ # ClassMethod UploadPackage(
25
+ # namespace As %String,
26
+ # body As %DynamicArray) As %DynamicObject
27
+ # {
28
+ # // check for namespace existence and user permissions against namespace
29
+ # If '..NamespaceCheck(namespace) {
30
+ # Return ""
31
+ # }
32
+ # New $NAMESPACE
33
+ # Set $NAMESPACE = namespace
34
+
35
+ # //Create directory for custom packages
36
+ # Do ##class(%ZHSLIB.HealthShareMgr).GetDBNSInfo(namespace,.out)
37
+ # Set customPackagesPath = ##class(%Library.File).NormalizeDirectory("fhir_packages", out.globalsDatabase.directory)
38
+ # If '##class(%Library.File).DirectoryExists(customPackagesPath) {
39
+ # If '##class(%Library.File).CreateDirectory(customPackagesPath) {
40
+ # $$$ThrowStatus($$$ERROR($$$DirectoryCannotCreate, customPackagesPath))
41
+ # }
42
+ # }
43
+
44
+ # //Find package name
45
+ # Set iterator = body.%GetIterator()
46
+ # Set packageName = ""
47
+ # While iterator.%GetNext(, .fileObject ) {
48
+ # If fileObject.name = "package.json" {
49
+ # Set packageName = fileObject.data.name_"@"_fileObject.data.version
50
+ # }
51
+ # }
52
+ # If packageName = "" {
53
+ # Do ..%ReportRESTError($$$HTTP400,$$$ERROR($$$HSFHIRErrPackageNotFound))
54
+ # Return ""
55
+ # }
56
+
57
+ # Set packagePath = ##class(%Library.File).NormalizeDirectory(packageName, customPackagesPath)
58
+ # // If the package already exists then we must be meaning to re-load it. Delete files/directory/metadata and recreate fresh.
59
+ # If ##class(%Library.File).DirectoryExists(packagePath) {
60
+ # If '##class(%Library.File).RemoveDirectoryTree(packagePath) {
61
+ # $$$ThrowStatus($$$ERROR($$$DirectoryPermission , packagePath))
62
+ # }
63
+ # }
64
+ # If '##class(%Library.File).CreateDirectory(packagePath) {
65
+ # $$$ThrowStatus($$$ERROR($$$DirectoryCannotCreate, customPackagesPath))
66
+ # }
67
+ # Set pkg = ##class(HS.FHIRMeta.Storage.Package).FindById(packageName)
68
+ # If $ISOBJECT(pkg) {
69
+ # // Will fail and throw if the package is in-use or has dependencies preventing it from being deleted.
70
+ # Do ##class(HS.FHIRServer.ServiceAdmin).DeleteMetadataPackage(packageName)
71
+ # }
72
+ # Kill pkg
73
+
74
+ # //Unpack JSON objects
75
+ # Set iterator = body.%GetIterator()
76
+ # While iterator.%GetNext(.key , .fileObject ) {
77
+ # Set fileName = ##class(%Library.File).NormalizeFilename(fileObject.name,packagePath)
78
+ # Set fileStream = ##class(%Stream.FileCharacter).%New()
79
+ # Set fileStream.TranslateTable = "UTF8"
80
+ # $$$ThrowOnError(fileStream.LinkToFile(fileName))
81
+ # Do fileObject.data.%ToJSON(.fileStream)
82
+ # $$$ThrowOnError(fileStream.%Save())
83
+ # }
84
+
85
+ # //Import package
86
+ # Do ##class(HS.FHIRMeta.Load.NpmLoader).importPackages(packagePath)
87
+ # Set pkg = ..GetOnePackage(packageName, namespace)
88
+ # Do ..%SetStatusCode($$$HTTP201)
89
+ # Do ..%SetHeader("location", %request.Application _ "packages/" _ packageName _ "?namespace=" _ namespace)
90
+ # Return pkg
91
+ # }
@@ -6,14 +6,24 @@ import importlib.resources
6
6
  import json
7
7
  import inspect
8
8
  import ast
9
- from typing import Any, Dict, Optional, Union, Tuple
9
+ from typing import Any, Dict, Optional, Union, Tuple, TypedDict
10
10
 
11
11
  import xmltodict
12
+ import requests
12
13
  from pydantic import TypeAdapter
13
14
 
14
15
  from . import _iris
15
16
  from ._message import _Message, _PydanticMessage
16
17
 
18
+ class RemoteSettings(TypedDict, total=False):
19
+ """Typed dictionary for remote migration settings."""
20
+ url: str # Required: the host url to connect to
21
+ namespace: str # Optional: the namespace to use (default: 'USER')
22
+ package: str # Optional: the package to use (default: 'python')
23
+ remote_folder: str # Optional: the folder to use (default: '')
24
+ username: str # Optional: the username to use to connect (default: '')
25
+ password: str # Optional: the password to use to connect (default: '')
26
+
17
27
  class _Utils():
18
28
  @staticmethod
19
29
  def raise_on_error(sc):
@@ -250,6 +260,83 @@ class _Utils():
250
260
 
251
261
  return module
252
262
 
263
+ @staticmethod
264
+ def migrate_remote(filename=None):
265
+ """
266
+ Read a settings file from the filename
267
+ If the settings.py file has a key 'REMOTE_SETTINGS' then it will use the value of that key
268
+ as the remote host to connect to.
269
+ the REMOTE_SETTINGS is a RemoteSettings dictionary with the following keys:
270
+ * 'url': the host url to connect to (mandatory)
271
+ * 'namespace': the namespace to use (optional, default is 'USER')
272
+ * 'package': the package to use (optional, default is 'python')
273
+ * 'remote_folder': the folder to use (optional, default is '')
274
+ * 'username': the username to use to connect (optional, default is '')
275
+ * 'password': the password to use to connect (optional, default is '')
276
+
277
+ The remote host is a rest API that will be used to register the components
278
+ The payload will be a json object with the following keys:
279
+ * 'namespace': the namespace to use
280
+ * 'package': the package to use
281
+ * 'body': the body of the request, it will be a json object with the following keys:
282
+ * 'name': name of the file
283
+ * 'data': the data of the file, it will be an UTF-8 encoded string
284
+
285
+ 'body' will be constructed with all the files in the folder if the folder is not empty else use root folder of settings.py
286
+ """
287
+ settings, path = _Utils._load_settings(filename)
288
+ remote_settings: Optional[RemoteSettings] = getattr(settings, 'REMOTE_SETTINGS', None) if settings else None
289
+
290
+ if not remote_settings:
291
+ _Utils.migrate(filename)
292
+ return
293
+
294
+ # Validate required fields
295
+ if 'url' not in remote_settings:
296
+ raise ValueError("REMOTE_SETTINGS must contain 'url' field")
297
+
298
+ # prepare the payload with defaults
299
+ payload = {
300
+ 'namespace': remote_settings.get('namespace', 'USER'),
301
+ 'package': remote_settings.get('package', 'python'),
302
+ 'remote_folder': remote_settings.get('remote_folder', ''),
303
+ 'body': []
304
+ }
305
+
306
+ # get the folder to register
307
+ folder = _Utils._get_folder_path(filename, path)
308
+
309
+ # iterate over all files in the folder
310
+ for root, _, files in os.walk(folder):
311
+ for file in files:
312
+ if file.endswith('.py') or file.endswith('.cls'):
313
+ file_path = os.path.join(root, file)
314
+ relative_path = os.path.relpath(file_path, folder)
315
+ # Normalize path separators for cross-platform compatibility
316
+ relative_path = relative_path.replace(os.sep, '/')
317
+ with open(file_path, 'r', encoding='utf-8') as f:
318
+ data = f.read()
319
+ payload['body'].append({
320
+ 'name': relative_path,
321
+ 'data': data
322
+ })
323
+
324
+ # send the request to the remote settings
325
+ response = requests.put(
326
+ url=f"{remote_settings['url']}/api/iop/migrate",
327
+ json=payload,
328
+ headers={
329
+ 'Content-Type': 'application/json',
330
+ 'Accept': 'application/json'
331
+ },
332
+ auth=(remote_settings.get('username', ''), remote_settings.get('password', '')),
333
+ timeout=10
334
+ )
335
+
336
+ print(f"Response from remote migration: {response.text}")
337
+
338
+ response.raise_for_status() # Raise an error for bad responses
339
+
253
340
  @staticmethod
254
341
  def migrate(filename=None):
255
342
  """
@@ -265,43 +352,96 @@ class _Utils():
265
352
  * SCHEMAS
266
353
  List of classes
267
354
  """
268
- path = None
269
- # try to load the settings file
355
+ settings, path = _Utils._load_settings(filename)
356
+
357
+ _Utils._register_settings_components(settings, path)
358
+
359
+ _Utils._cleanup_sys_path(path)
360
+
361
+ @staticmethod
362
+ def _load_settings(filename):
363
+ """Load settings module from file or default location.
364
+
365
+ Returns:
366
+ tuple: (settings_module, path_added_to_sys)
367
+ """
368
+ path_added = None
369
+
270
370
  if filename:
271
371
  # check if the filename is absolute or relative
272
- if os.path.isabs(filename):
273
- path = os.path.dirname(filename)
274
- else:
372
+ if not os.path.isabs(filename):
275
373
  raise ValueError("The filename must be absolute")
374
+
276
375
  # add the path to the system path to the beginning
277
- sys.path.insert(0,os.path.normpath(path))
376
+ path_added = os.path.normpath(os.path.dirname(filename))
377
+ sys.path.insert(0, path_added)
278
378
  # import settings from the specified file
279
- settings = _Utils.import_module_from_path('settings',filename)
379
+ settings = _Utils.import_module_from_path('settings', filename)
280
380
  else:
281
381
  # import settings from the settings module
282
- import settings # type: ignore
283
- # get the path of the settings file
284
- path = os.path.dirname(inspect.getfile(settings))
382
+ import settings # type: ignore
383
+
384
+ return settings, path_added
385
+
386
+ @staticmethod
387
+ def _get_folder_path(filename, path_added_to_sys):
388
+ """Get the folder path for migration operations.
389
+
390
+ Args:
391
+ filename: Original filename parameter
392
+ path_added_to_sys: Path that was added to sys.path
393
+
394
+ Returns:
395
+ str: Folder path to use for migration
396
+ """
397
+ if filename:
398
+ return os.path.dirname(filename)
399
+ else:
400
+ return os.getcwd()
401
+
402
+ @staticmethod
403
+ def _register_settings_components(settings, path):
404
+ """Register all components from settings (classes, productions, schemas).
405
+
406
+ Args:
407
+ settings: Settings module containing CLASSES, PRODUCTIONS, SCHEMAS
408
+ path: Base path for component registration
409
+ """
410
+ # Use settings file location if path not provided
411
+ if not path:
412
+ path = os.path.dirname(inspect.getfile(settings))
413
+
285
414
  try:
286
415
  # set the classes settings
287
- _Utils.set_classes_settings(settings.CLASSES,path)
416
+ _Utils.set_classes_settings(settings.CLASSES, path)
288
417
  except AttributeError:
289
418
  print("No classes to register")
419
+
290
420
  try:
291
421
  # set the productions settings
292
- _Utils.set_productions_settings(settings.PRODUCTIONS,path)
422
+ _Utils.set_productions_settings(settings.PRODUCTIONS, path)
293
423
  except AttributeError:
294
424
  print("No productions to register")
425
+
295
426
  try:
296
427
  # set the schemas
297
428
  for cls in settings.SCHEMAS:
298
429
  _Utils.register_message_schema(cls)
299
430
  except AttributeError:
300
431
  print("No schemas to register")
301
- try:
302
- sys.path.remove(os.path.normpath(path))
303
- except ValueError:
304
- pass
432
+
433
+ @staticmethod
434
+ def _cleanup_sys_path(path):
435
+ """Remove path from sys.path if it was added.
436
+
437
+ Args:
438
+ path: Path to remove from sys.path
439
+ """
440
+ if path:
441
+ try:
442
+ sys.path.remove(os.path.normpath(path))
443
+ except ValueError:
444
+ pass
305
445
 
306
446
  @staticmethod
307
447
  def import_module_from_path(module_name, file_path):
@@ -0,0 +1,49 @@
1
+ Class IOP.Projection Extends %Projection.AbstractProjection
2
+ {
3
+
4
+ /// Projection to make install on compile
5
+ Projection Reference As Projection;
6
+
7
+ /// Call of the projection
8
+ ClassMethod CreateProjection(
9
+ cls As %String,
10
+ ByRef params) As %Status
11
+ {
12
+ set tSC = $$$OK
13
+ set tCurrentNamespace = $Namespace
14
+
15
+ Try {
16
+ set $NAMESPACE = "%SYS"
17
+ // Create a new Web App for IOP Remote Handler
18
+ set webName = "/api/iop"
19
+ set webProperties("NameSpace") = tCurrentNamespace
20
+ set webProperties("Enabled") = 1
21
+ set webProperties("DispatchClass") = "IOP.Service.Remote.Handler"
22
+ set webProperties("AutheEnabled") = 32
23
+ set webProperties("ServeFiles")=2
24
+ set webProperties("Recurse")=1
25
+ if ##class(Security.Applications).Exists(webName) {
26
+ // If the web application already exists, delete it first
27
+ $$$ThrowOnError(##class(Security.Applications).Delete(webName))
28
+ }
29
+ $$$ThrowOnError(##class(Security.Applications).Create(webName, .webProperties))
30
+ }
31
+ Catch ex {
32
+
33
+ Write !,"Error creating web application: ", ex.DisplayString(), !
34
+
35
+ }
36
+
37
+ znspace tCurrentNamespace
38
+ Return tSC
39
+ }
40
+
41
+ ClassMethod RemoveProjection(
42
+ cls As %String,
43
+ ByRef params,
44
+ recompile As %Boolean) As %Status
45
+ {
46
+ Quit $$$OK
47
+ }
48
+
49
+ }
@@ -0,0 +1,30 @@
1
+ Class IOP.Service.Remote.Handler Extends %CSP.REST
2
+ {
3
+
4
+ Parameter CHARSET = "utf-8";
5
+
6
+ Parameter CONVERTINPUTSTREAM = 1;
7
+
8
+ Parameter CONTENTTYPE = "application/json";
9
+
10
+ /// This parameter influences the CORS support. The default is an empty string meaning 'not specified'.
11
+ /// If set to true (1) then CORS processing is ON. If set to false (0) then CORS processing is OFF.
12
+ /// If left unset "" then the decision to process CORS is delegated to the setting on the URL map route.
13
+ Parameter HandleCorsRequest = 1;
14
+
15
+ Parameter UseSession As Integer = 0;
16
+
17
+ /// Ignore any writes done directly by the REST method.
18
+ Parameter IgnoreWrites = 0;
19
+
20
+ XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
21
+ {
22
+ <Routes>
23
+ <!-- Iop Management -->
24
+ <Map Prefix="/v1" Forward="IOP.Service.Remote.Rest.v1"/>
25
+ <!-- make the default forward to the highest current version of the API -->
26
+ <Map Prefix="/*" Forward="IOP.Service.Remote.Rest.v1"/>
27
+ </Routes>
28
+ }
29
+
30
+ }
@@ -0,0 +1,97 @@
1
+ Class IOP.Service.Remote.Rest.v1 Extends %CSP.REST
2
+ {
3
+
4
+ XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
5
+ {
6
+ <Routes>
7
+ <Route Url="/migrate" Method="PUT" Call="PutMigrate"/>
8
+ <Route Url="/version" Method="GET" Call="GetVersion"/>
9
+ </Routes>
10
+ }
11
+
12
+ ClassMethod GetVersion() As %String
13
+ {
14
+ // Return the version of the IOP service
15
+ Return {
16
+ "version": "1.0.0",
17
+ "description": "Interoperability Embedded Python Service API"
18
+ }.%ToJSON()
19
+ }
20
+
21
+ ClassMethod NamespaceCheck(pNamespace As %String) As %Boolean [ Internal, Private ]
22
+ {
23
+ If '##class(%SYS.Namespace).Exists(pNamespace) {
24
+ throw $$$ERROR("NamespaceDoesNotExist")
25
+ }
26
+ Return 1
27
+ }
28
+
29
+ ClassMethod PutMigrate() As %DynamicObject
30
+ {
31
+ Try {
32
+ // Get the request body
33
+ set dyna = {}.%FromJSON(%request.Content)
34
+ set body = dyna.%Get("body")
35
+ set namespace = dyna.%Get("namespace")
36
+ set targetDirectory = dyna.%Get("remote_folder")
37
+ set packageName = dyna.%Get("package")
38
+ // check for namespace existence and user permissions against namespace
39
+ If '..NamespaceCheck(namespace) {
40
+ Return ""
41
+ }
42
+ New $NAMESPACE
43
+ Set $NAMESPACE = namespace
44
+
45
+
46
+ //Create directory for custom packages
47
+ If targetDirectory '= "" {
48
+ If '##class(%Library.File).DirectoryExists(targetDirectory) {
49
+ If '##class(%Library.File).CreateDirectory(targetDirectory) {
50
+ $$$ThrowStatus($$$ERROR($$$DirectoryCannotCreate, targetDirectory))
51
+ }
52
+ }
53
+ }
54
+ Else {
55
+ // Set targetDirectory to the CODE DATABASE directory
56
+ do ##class(%SYS.Namespace).GetAllNSInfo("USER",.info)
57
+ Set targetDirectory = info("RoutineDB","Directory")
58
+ }
59
+
60
+ Set packagePath = ##class(%Library.File).NormalizeDirectory(packageName, targetDirectory)
61
+ // If the package already exists then we must be meaning to re-load it. Delete files/directory/metadata and recreate fresh.
62
+ If ##class(%Library.File).DirectoryExists(packagePath) {
63
+ If '##class(%Library.File).RemoveDirectoryTree(packagePath) {
64
+ $$$ThrowStatus($$$ERROR($$$DirectoryPermission , packagePath))
65
+ }
66
+ }
67
+ If '##class(%Library.File).CreateDirectory(packagePath) {
68
+ $$$ThrowStatus($$$ERROR($$$DirectoryCannotCreate, packagePath))
69
+ }
70
+
71
+ //Unpack JSON objects
72
+ Set iterator = body.%GetIterator()
73
+ While iterator.%GetNext(.key , .fileObject ) {
74
+ // If fileObject.name has '/' then it is a path, we need to normalize it
75
+ Set fileName = ##class(%Library.File).NormalizeFilename(fileObject.name,packagePath)
76
+ do ##class(%Library.File).CreateDirectoryChain(##class(%Library.File).GetDirectory(fileName))
77
+ Set fileStream = ##class(%Stream.FileCharacter).%New()
78
+ Set fileStream.TranslateTable = "UTF8"
79
+ $$$ThrowOnError(fileStream.LinkToFile(fileName))
80
+ Do fileStream.Write(fileObject.data)
81
+ $$$ThrowOnError(fileStream.%Save())
82
+ }
83
+
84
+ //Do the iop migration
85
+
86
+ set iopUtils = ##class(IOP.Wrapper).Import("iop._utils")
87
+ do iopUtils."_Utils".migrate(##class(%Library.File).NormalizeFilename("settings.py", packagePath))
88
+ }
89
+ Catch ex {
90
+ $$$ThrowOnError(ex.AsStatus())
91
+ }
92
+
93
+
94
+ return $$$OK
95
+ }
96
+
97
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iris_pex_embedded_python
3
- Version: 3.5.1b4
3
+ Version: 3.5.2
4
4
  Summary: Iris Interoperability based on Embedded Python
5
5
  Author-email: grongier <guillaume.rongier@intersystems.com>
6
6
  License: MIT License
@@ -53,6 +53,7 @@ src/iop/_message_validator.py
53
53
  src/iop/_outbound_adapter.py
54
54
  src/iop/_private_session_duplex.py
55
55
  src/iop/_private_session_process.py
56
+ src/iop/_remote.py
56
57
  src/iop/_serialization.py
57
58
  src/iop/_utils.py
58
59
  src/iop/cls/IOP/BusinessOperation.cls
@@ -64,6 +65,7 @@ src/iop/cls/IOP/InboundAdapter.cls
64
65
  src/iop/cls/IOP/Message.cls
65
66
  src/iop/cls/IOP/OutboundAdapter.cls
66
67
  src/iop/cls/IOP/PickleMessage.cls
68
+ src/iop/cls/IOP/Projection.cls
67
69
  src/iop/cls/IOP/Test.cls
68
70
  src/iop/cls/IOP/Utils.cls
69
71
  src/iop/cls/IOP/Wrapper.cls
@@ -82,6 +84,8 @@ src/iop/cls/IOP/PrivateSession/Message/Poll.cls
82
84
  src/iop/cls/IOP/PrivateSession/Message/Start.cls
83
85
  src/iop/cls/IOP/PrivateSession/Message/Stop.cls
84
86
  src/iop/cls/IOP/Service/WSGI.cls
87
+ src/iop/cls/IOP/Service/Remote/Handler.cls
88
+ src/iop/cls/IOP/Service/Remote/Rest/v1.cls
85
89
  src/iop/wsgi/handlers.py
86
90
  src/iris_pex_embedded_python.egg-info/PKG-INFO
87
91
  src/iris_pex_embedded_python.egg-info/SOURCES.txt