purem 2.1.5__tar.gz → 3.0.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: purem
3
- Version: 2.1.5
3
+ Version: 3.0.1
4
4
  Summary: The official high-performance mapping function for mixed-type arrays powered by Work TIF Ltd.
5
5
  Author-email: Raman Marozau <raman@worktif.com>
6
6
  License-Expression: BUSL-1.1
@@ -26,7 +26,6 @@ Description-Content-Type: text/x-rst
26
26
  License-File: LICENSE
27
27
  Requires-Dist: numpy~=2.1.3
28
28
  Requires-Dist: pytest~=8.3.5
29
- Requires-Dist: torch~=2.6.0
30
29
  Requires-Dist: numba~=0.61.0
31
30
  Requires-Dist: certifi~=2025.1.31
32
31
  Requires-Dist: pydantic~=2.10.6
purem-3.0.1/VERSION ADDED
@@ -0,0 +1 @@
1
+ 3.0.1
@@ -0,0 +1,339 @@
1
+ """
2
+ Business Source License 1.1
3
+
4
+ Copyright (C) 2025 Raman Marozau, raman@worktif.com
5
+ Use of this software is governed by the Business Source License included in the LICENSE file and at www.mariadb.com/bsl11.
6
+
7
+ Change Date: Never
8
+ On the date above, in accordance with the Business Source License, use of this software will be governed by the open source license specified in the LICENSE file.
9
+ Additional Use Grant: Free for personal and non-commercial research use only.
10
+
11
+ SPDX-License-Identifier: BUSL-1.1
12
+ """
13
+
14
+ import base64
15
+ import ctypes
16
+ import json
17
+ import os
18
+ import shutil
19
+ import ssl
20
+ import urllib.request
21
+ import zipfile
22
+ from typing import List, Optional, Dict
23
+
24
+ import certifi as certifi
25
+ import numpy as np
26
+ from numpy import ndarray
27
+
28
+ from purem.env_config import load_env_config
29
+ from purem.file_structure import FileStructure
30
+ from purem.loader import Loader
31
+ from purem.logger import Logger
32
+ from purem.utils import _compute_shifted_jit
33
+
34
+
35
+ class Purem:
36
+ """
37
+ Represents the functionality of the Purem system, including initialization, configuration,
38
+ and operations using a shared library binary. The class provides methods for setting up
39
+ license keys, managing binary paths, loading configurations from a Content Delivery Network (CDN),
40
+ and executing specific computations such as the softmax function. It handles both high-level
41
+ configuration details and low-level interactions with binary libraries.
42
+
43
+ :ivar _license_key: The license key used to initialize the system.
44
+ :type _license_key: Optional[str]
45
+ :ivar _lib: Represents the dynamically loaded shared library for performing operations.
46
+ :type _lib: Optional[ctypes.CDLL]
47
+ :ivar _download_binary_url: URL for downloading the binary configuration.
48
+ :type _download_binary_url: Optional[str]
49
+ :ivar _ctx: SSL context for establishing secure connections.
50
+ :type _ctx: ssl.SSLContext
51
+ :ivar _file_structure: The handler for managing binary-related paths and file structure.
52
+ :type _file_structure: FileStructure
53
+ :ivar _binary_path: Absolute path of the binary file used for execution.
54
+ :type _binary_path: str
55
+ :ivar _binary_project_root_path: Path to the binary located in the project root.
56
+ :type _binary_project_root_path: str
57
+ :ivar _binary_archive_path: Path where binary archives are stored.
58
+ :type _binary_archive_path: str
59
+ :ivar _binary_archive_path_tmp: Temporary path for binary archive operations.
60
+ :type _binary_archive_path_tmp: str
61
+ :ivar _env: Object holding environment configurations and variables.
62
+ :type _env: Any
63
+ :ivar _config_url: URL for retrieving Purem's configuration from a remote server.
64
+ :type _config_url: str
65
+ :ivar _loader: Loader instance for displaying runtime messages during setup and initialization.
66
+ :type _loader: Loader
67
+ :ivar _log: Logger instance for recording system messages and error details.
68
+ :type _log: Logger
69
+ """
70
+
71
+ def __init__(self, licenced_key: Optional[str] = None):
72
+ """
73
+ Represents the initialization and configuration of an environment, license key,
74
+ and file structure required for the system's binary operations.
75
+
76
+ :param licenced_key: Optional license key string for initializing the system.
77
+ :type licenced_key: Optional[str]
78
+ """
79
+ self._license_key = licenced_key or None
80
+ self._lib = None
81
+ self._download_binary_url = None
82
+ self._ctx = ssl.create_default_context(cafile=certifi.where())
83
+ self._file_structure = FileStructure()
84
+ self._binary_path = self._file_structure.get_binary_path()
85
+ self._binary_project_root_path = (
86
+ self._file_structure.get_binary_project_root_path()
87
+ )
88
+ self._binary_archive_path = self._file_structure.get_binary_archive_path()
89
+ self._binary_archive_path_tmp = (
90
+ self._file_structure.get_binary_archive_path_tmp()
91
+ )
92
+ self._env = load_env_config()
93
+ self._config_url = (
94
+ self._env.PUREM_CONFIG_URL
95
+ or "https://api.worktif.com/v2/portal/products/purem/config"
96
+ )
97
+ self._loader = Loader()
98
+ self._log = Logger()
99
+
100
+ def configure(self, license_key: Optional[str] = None) -> None:
101
+ """
102
+ Configures the system with a given license key.
103
+
104
+ This method sets up the license key required for initializing the system. If no
105
+ license key is provided during configuration, the method checks if it was
106
+ already initialized. If the license key remains unset, it raises a ValueError
107
+ to indicate that a valid license key is mandatory for the setup.
108
+
109
+ :raises ValueError: Raised when no valid license key is provided during
110
+ configuration.
111
+
112
+ :param license_key: An optional string representing the license key.
113
+ :return: None
114
+ """
115
+ if self._license_key is None and license_key is not None:
116
+ self._license_key = license_key
117
+ if self._license_key is None:
118
+ raise ValueError(
119
+ self._log.info(
120
+ "Purem requires a valid license key to initialize.\n"
121
+ "You can obtain your key at https://worktif.com or through your enterprise deployment."
122
+ )
123
+ )
124
+
125
+ self._set_binary()
126
+
127
+ def softmax(self, array: ndarray) -> ndarray:
128
+ ptr = array.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
129
+ self._lib.purem(ptr, array.size)
130
+ return array
131
+
132
+ def softmax_pure(self, ptr, size) -> None:
133
+ """
134
+ Computes the softmax function on the provided array using pure library implementation.
135
+
136
+ This function applies the softmax transformation to the data pointed to by the
137
+ pointer and modifies it in-place. The underlying implementation is handled
138
+ by a pure library function call.
139
+
140
+ :param ptr: Pointer to the data array that the softmax is to be applied on.
141
+ :type ptr: Any
142
+ :param size: The number of elements in the data array to be processed.
143
+ :type size: int
144
+ :return: None
145
+ """
146
+ self._lib.purem(ptr, size)
147
+
148
+ def _build_url(self, config: dict) -> Optional[str]:
149
+ """
150
+ Constructs a URL based on the provided configuration dictionary. The method
151
+ combines protocol, base URL, path, and appends a license key to generate
152
+ a complete binary URL. If the provided configuration is None, the method
153
+ returns None immediately.
154
+
155
+ :param config: A dictionary containing the parts needed to construct the
156
+ URL. The keys usually include:
157
+ - `base_url`: The base of the URL (e.g., domain).
158
+ - `protocol`: The URL protocol (e.g., "http" or "https").
159
+ - `pathname`: The path to append to the base URL.
160
+ :return: Returns the complete binary URL as a string if the configuration
161
+ is valid. If the given configuration is None, it returns None.
162
+ """
163
+ if config is None:
164
+ return None
165
+ base = config.get("base_url", "").rstrip("/")
166
+ protocol = config.get("protocol", "https")
167
+ path = config.get("pathname", "").lstrip("/")
168
+ binary_url = f"{protocol}://{base}/{path}{self._license_key}"
169
+ return binary_url
170
+
171
+ def _tune_binary(self):
172
+ """
173
+ Tunes the binary by loading the specified binary file as a shared library
174
+ and setting up its function signatures for further use.
175
+
176
+ This method initializes the shared library from the specified `_binary_path`
177
+ and configures the expected argument types and return type for the `purem` function
178
+ provided by the library.
179
+
180
+ :raises OSError: If the library at `_binary_path` cannot be loaded by `ctypes.CDLL`.
181
+
182
+ :rtype: None
183
+ """
184
+ self._lib = ctypes.CDLL(str(self._binary_path))
185
+ self._lib.purem.argtypes = [ctypes.POINTER(ctypes.c_float), ctypes.c_size_t]
186
+ self._lib.purem.restype = None
187
+
188
+ def _tune_project_root_binary(self):
189
+ """
190
+ Tuning the project root binary configuration.
191
+
192
+ This private method initializes a connection to the binary library
193
+ located at the project's root path. It achieves this by loading the
194
+ binary through the use of the `ctypes.CDLL` method. The method also
195
+ sets expected argument types and a return type for a specific function
196
+ available in the shared library.
197
+
198
+ :return: None
199
+ """
200
+ self._lib = ctypes.CDLL(str(self._binary_project_root_path))
201
+ self._lib.purem.argtypes = [ctypes.POINTER(ctypes.c_float), ctypes.c_size_t]
202
+ self._lib.purem.restype = None
203
+
204
+ def _load_from_latest_cdn_index(self) -> Optional[Dict]:
205
+ """
206
+ Attempts to load the latest configuration from a CDN index, if a config URL is provided.
207
+ The method sends a request to the configured URL, reads its response, and parses it into
208
+ a dictionary. If there is no configured URL or an exception occurs during the process, it
209
+ returns None.
210
+
211
+ :raises Exception: If an error occurs during the request or while reading the response.
212
+
213
+ :return: A dictionary containing the parsed configuration data if the operation is
214
+ successful, or None if no config URL is provided or an error occurs.
215
+ :rtype: Optional[Dict]
216
+ """
217
+ try:
218
+ if self._config_url is not None:
219
+ req = urllib.request.Request(
220
+ self._config_url,
221
+ )
222
+
223
+ with urllib.request.urlopen(req, context=self._ctx) as response:
224
+ return json.load(response)
225
+ else:
226
+ return None
227
+
228
+ except Exception:
229
+ return None
230
+
231
+ def _set_binary(self):
232
+ """
233
+ Sets and initializes the binary for the Purem runtime environment. The method ensures that a valid binary
234
+ is available and properly configured. If a valid license key or binary is not found, the method will attempt
235
+ to download, validate, and extract the required binary files. If initialization fails at any stage,
236
+ appropriate errors are raised with detailed logging for debugging and support purposes.
237
+
238
+ :param self: Represents the instance of the class.
239
+ :type self: PuremRuntime
240
+
241
+ :raises ValueError: Raised if a valid license key is missing and cannot proceed with initialization.
242
+ :raises RuntimeError: Raised if the purem binary fails to load or cannot be initialized due to local issues,
243
+ license mismatch, or any other unexpected errors.
244
+
245
+ :return: None
246
+ """
247
+ if os.path.exists(self._binary_path):
248
+ self._tune_binary()
249
+ elif os.path.exists(self._binary_project_root_path):
250
+ self._tune_project_root_binary()
251
+ elif not self._license_key:
252
+ raise ValueError(
253
+ self._log.info(
254
+ "Purem requires a valid license key to initialize.\n"
255
+ "You can obtain your key at https://worktif.com or through your enterprise deployment."
256
+ )
257
+ )
258
+ else:
259
+ try:
260
+ self._loader.set_message(
261
+ "Initializing Purem licensed runtime locally..."
262
+ )
263
+ self._loader.start()
264
+ self._download_binary_url = (
265
+ self._build_url(self._load_from_latest_cdn_index())
266
+ or f"{self._env.PUREM_DOWNLOAD_BINARY_URL}{self._license_key}"
267
+ )
268
+ self._download_and_extract_binary()
269
+ self._loader.stop()
270
+
271
+ except Exception as e:
272
+ raise RuntimeError(
273
+ self._log.info(
274
+ "We couldn't load your Purem binary at this time.\n"
275
+ "This may be a local issue or license mismatch.\n"
276
+ "Please try again – or contact us at support@worktif.com.\n"
277
+ "We're here to help you run at full power."
278
+ )
279
+ )
280
+
281
+ try:
282
+ self._tune_project_root_binary()
283
+ except Exception as e:
284
+ raise RuntimeError(
285
+ self._log.info(
286
+ "It appears your Purem licensed binary can not be loaded. Please try again. If the problem "
287
+ "persists, please contact us at support@worktif.com. Thank you for your patience."
288
+ )
289
+ )
290
+
291
+ def _download_and_extract_binary(self):
292
+ """
293
+ Downloads a binary file from a given URL, saves it temporarily, and extracts its
294
+ contents to a specific directory. Handles errors related to incomplete or
295
+ corrupted downloads and archives.
296
+
297
+ Raises runtime errors with detailed context if an issue occurs during download
298
+ or extraction. Ensures successful extraction and logs the output location.
299
+
300
+ :raises RuntimeError: If the download or extraction process fails due to
301
+ corrupted archive or any other unexpected issue.
302
+ """
303
+ req = urllib.request.Request(
304
+ self._download_binary_url, headers={"User-Agent": "Mozilla/5.0"}
305
+ )
306
+
307
+ try:
308
+ with urllib.request.urlopen(req, context=self._ctx) as response:
309
+ with open(self._binary_archive_path_tmp, "wb") as out_file:
310
+ shutil.copyfileobj(response, out_file)
311
+
312
+ shutil.move(self._binary_archive_path_tmp, self._binary_archive_path)
313
+ except Exception as e:
314
+ raise RuntimeError(
315
+ self._log.info(
316
+ f"The Purem archive appears to be corrupted or incomplete.\nDetails: {e}"
317
+ "Please ensure the package downloaded fully and is unmodified.\n"
318
+ "Need help? Contact support@worktif.com – we'll assist right away.\n"
319
+ )
320
+ )
321
+
322
+ try:
323
+ with zipfile.ZipFile(self._binary_archive_path, "r") as zip_ref:
324
+ zip_ref.extractall(
325
+ self._file_structure.dirs.project_root_binary_dir_path
326
+ )
327
+ self._log.info_new_line(
328
+ f"Purem binary extracted to: {self._file_structure.dirs.binary_dir_path}"
329
+ )
330
+ except zipfile.BadZipFile as e:
331
+ raise RuntimeError(
332
+ self._log.info(
333
+ f"The Purem archive appears to be corrupted or incomplete.\nDetails: {e}"
334
+ "Please ensure the package downloaded fully and is unmodified.\n"
335
+ "Need help? Contact support@worktif.com – we'll assist right away.\n"
336
+ )
337
+ )
338
+
339
+ self._binary_archive_path.unlink()
@@ -2,7 +2,7 @@
2
2
  Business Source License 1.1
3
3
 
4
4
  Copyright (C) 2025 Raman Marozau, raman@worktif.com
5
- Use of this software is governed by the Business Source License included in the LICENSE.TXT file and at www.mariadb.com/bsl11.
5
+ Use of this software is governed by the Business Source License included in the LICENSE file and at www.mariadb.com/bsl11.
6
6
 
7
7
  Change Date: Never
8
8
  On the date above, in accordance with the Business Source License, use of this software will be governed by the open source license specified in the LICENSE file.
@@ -2,7 +2,7 @@
2
2
  Business Source License 1.1
3
3
 
4
4
  Copyright (C) 2025 Raman Marozau, raman@worktif.com
5
- Use of this software is governed by the Business Source License included in the LICENSE.TXT file and at www.mariadb.com/bsl11.
5
+ Use of this software is governed by the Business Source License included in the LICENSE file and at www.mariadb.com/bsl11.
6
6
 
7
7
  Change Date: Never
8
8
  On the date above, in accordance with the Business Source License, use of this software will be governed by the open source license specified in the LICENSE file.
@@ -0,0 +1,102 @@
1
+ """
2
+ Business Source License 1.1
3
+
4
+ Copyright (C) 2025 Raman Marozau, raman@worktif.com
5
+ Use of this software is governed by the Business Source License included in the LICENSE file and at www.mariadb.com/bsl11.
6
+
7
+ Change Date: Never
8
+ On the date above, in accordance with the Business Source License, use of this software will be governed by the open source license specified in the LICENSE file.
9
+ Additional Use Grant: Free for personal and non-commercial research use only.
10
+
11
+ SPDX-License-Identifier: BUSL-1.1
12
+ """
13
+
14
+ import sys
15
+ import threading
16
+ import time
17
+
18
+
19
+ class Loader:
20
+ def __init__(self, message="Downloading"):
21
+ """
22
+ Represents a CLI-based animation that indicates a background task.
23
+
24
+ This class is designed to show a simple animation in the terminal
25
+ to convey the progress of an ongoing background task. The animation
26
+ runs in a separate thread to ensure that it does not block the main
27
+ thread's operations.
28
+
29
+ Attributes
30
+ ----------
31
+ _message : str
32
+ The text message displayed along with the animation.
33
+ _thread : threading.Thread
34
+ The thread responsible for running the animation in the background.
35
+ done : bool
36
+ A control flag to stop the animation when set to True.
37
+
38
+ :param message: The message to display alongside the animation. Defaults to
39
+ "Downloading".
40
+ """
41
+ self._message = message
42
+ self._thread = threading.Thread(target=self._animate, daemon=True)
43
+ self.done = False
44
+
45
+ def set_message(self, message: str):
46
+ """
47
+ Sets the message attribute of the instance.
48
+
49
+ :param message: A string representing the message to be assigned.
50
+ :type message: str
51
+ """
52
+ self._message = message
53
+
54
+ def start(self):
55
+ """
56
+ Starts the thread associated with this instance.
57
+
58
+ This method initiates the thread's execution by invoking the `start`
59
+ method on the `self._thread` object. It assumes that the `self._thread`
60
+ attribute has been properly initialized and is a valid thread instance.
61
+
62
+ :return: None
63
+ """
64
+ self._thread.start()
65
+
66
+ def stop(self):
67
+ """
68
+ Represents a mechanism to stop a thread execution gracefully.
69
+
70
+ This class or function provides a controlled way to stop a running thread
71
+ by marking it as done and waiting for the thread to conclude its execution.
72
+ It ensures the thread completes its ongoing tasks correctly before stopping.
73
+
74
+ :attributes:
75
+ done: Indicates whether the thread execution is flagged to stop.
76
+ _thread: The thread instance being managed.
77
+
78
+ :return: None
79
+ """
80
+ self.done = True
81
+ self._thread.join()
82
+
83
+ def _animate(self):
84
+ """
85
+ Handles the animation of a loading spinner for a CLI-based task. The animation
86
+ displays a series of symbols in rotation while the task is ongoing. It
87
+ continues until the flag `self.done` is set to True. Once the task is complete,
88
+ a "done" message is displayed.
89
+
90
+ This method is designed to provide a simple user feedback mechanism during
91
+ long-running or background operations in command-line applications.
92
+
93
+ :return: None
94
+ """
95
+ symbols = ["|", "/", "-", "\\"]
96
+ i = 0
97
+ while not self.done:
98
+ sys.stdout.write(f"\r{self._message}... {symbols[i % len(symbols)]}")
99
+ sys.stdout.flush()
100
+ i += 1
101
+ time.sleep(0.1)
102
+ sys.stdout.write(f"\r{self._message}... done.\n")
@@ -2,7 +2,7 @@
2
2
  Business Source License 1.1
3
3
 
4
4
  Copyright (C) 2025 Raman Marozau, raman@worktif.com
5
- Use of this software is governed by the Business Source License included in the LICENSE.TXT file and at www.mariadb.com/bsl11.
5
+ Use of this software is governed by the Business Source License included in the LICENSE file and at www.mariadb.com/bsl11.
6
6
 
7
7
  Change Date: Never
8
8
  On the date above, in accordance with the Business Source License, use of this software will be governed by the open source license specified in the LICENSE file.
@@ -2,7 +2,7 @@
2
2
  Business Source License 1.1
3
3
 
4
4
  Copyright (C) 2025 Raman Marozau, raman@worktif.com
5
- Use of this software is governed by the Business Source License included in the LICENSE.TXT file and at www.mariadb.com/bsl11.
5
+ Use of this software is governed by the Business Source License included in the LICENSE file and at www.mariadb.com/bsl11.
6
6
 
7
7
  Change Date: Never
8
8
  On the date above, in accordance with the Business Source License, use of this software will be governed by the open source license specified in the LICENSE file.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: purem
3
- Version: 2.1.5
3
+ Version: 3.0.1
4
4
  Summary: The official high-performance mapping function for mixed-type arrays powered by Work TIF Ltd.
5
5
  Author-email: Raman Marozau <raman@worktif.com>
6
6
  License-Expression: BUSL-1.1
@@ -26,7 +26,6 @@ Description-Content-Type: text/x-rst
26
26
  License-File: LICENSE
27
27
  Requires-Dist: numpy~=2.1.3
28
28
  Requires-Dist: pytest~=8.3.5
29
- Requires-Dist: torch~=2.6.0
30
29
  Requires-Dist: numba~=0.61.0
31
30
  Requires-Dist: certifi~=2025.1.31
32
31
  Requires-Dist: pydantic~=2.10.6
@@ -1,6 +1,5 @@
1
1
  numpy~=2.1.3
2
2
  pytest~=8.3.5
3
- torch~=2.6.0
4
3
  numba~=0.61.0
5
4
  certifi~=2025.1.31
6
5
  pydantic~=2.10.6
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "purem"
7
- version = "2.1.5"
7
+ version = "3.0.1"
8
8
  description = "The official high-performance mapping function for mixed-type arrays powered by Work TIF Ltd."
9
9
  readme = { file = "README.rst", content-type = "text/x-rst" }
10
10
  requires-python = ">=3.11"
@@ -33,7 +33,6 @@ classifiers = [
33
33
  dependencies = [
34
34
  "numpy~=2.1.3",
35
35
  "pytest~=8.3.5",
36
- "torch~=2.6.0",
37
36
  "numba~=0.61.0",
38
37
  "certifi~=2025.1.31",
39
38
  "pydantic~=2.10.6",
purem-2.1.5/VERSION DELETED
@@ -1 +0,0 @@
1
- 2.1.5
purem-2.1.5/purem/core.py DELETED
@@ -1,252 +0,0 @@
1
- """
2
- Business Source License 1.1
3
-
4
- Copyright (C) 2025 Raman Marozau, raman@worktif.com
5
- Use of this software is governed by the Business Source License included in the LICENSE.TXT file and at www.mariadb.com/bsl11.
6
-
7
- Change Date: Never
8
- On the date above, in accordance with the Business Source License, use of this software will be governed by the open source license specified in the LICENSE file.
9
- Additional Use Grant: Free for personal and non-commercial research use only.
10
-
11
- SPDX-License-Identifier: BUSL-1.1
12
- """
13
-
14
- import base64
15
- import ctypes
16
- import json
17
- import os
18
- import shutil
19
- import ssl
20
- import urllib.request
21
- import zipfile
22
- from typing import List, Optional, Dict
23
-
24
- import certifi as certifi
25
- import numpy as np
26
- from numpy import ndarray
27
-
28
- from purem.env_config import load_env_config
29
- from purem.file_structure import FileStructure
30
- from purem.loader import Loader
31
- from purem.logger import Logger
32
- from purem.utils import _compute_shifted_jit
33
-
34
-
35
- class Purem:
36
- """
37
- Summary of what the class does.
38
-
39
- The Purem class is used to manage and handle the initialization, configuration, and
40
- operation of the Purem environment and its associated runtime, including license management,
41
- binary handling, and API configuration. It centralizes the setup of the Purem runtime,
42
- ensuring that all necessary binaries, license checks, and configurations are in place.
43
-
44
- The class supports downloading and extracting binaries, setting up runtime libraries, and providing utility
45
- methods such as softmax computation, license validation, and URL construction to interact with remote
46
- and local systems.
47
-
48
- :ivar _license_key: The license key used for Purem initialization and validation.
49
- :type _license_key: Optional[str]
50
- :ivar _lib: The dynamically loaded library object for the Purem binary.
51
- :type _lib: ctypes.CDLL
52
- :ivar _download_binary_url: The URL for downloading the Purem binary.
53
- :type _download_binary_url: Optional[str]
54
- :ivar _ctx: SSL context used for secure connections, initialized using the system's certificates.
55
- :type _ctx: ssl.SSLContext
56
- :ivar _file_structure: Manages file paths and structures related to Purem binary and runtime.
57
- :type _file_structure: FileStructure
58
- :ivar _binary_path: The path to the main Purem binary.
59
- :type _binary_path: pathlib.Path
60
- :ivar _binary_project_root_path: The path to the project root Purem binary.
61
- :type _binary_project_root_path: pathlib.Path
62
- :ivar _binary_archive_path: The path where the binary archive is stored.
63
- :type _binary_archive_path: pathlib.Path
64
- :ivar _binary_archive_path_tmp: The temporary path for binary archive operations.
65
- :type _binary_archive_path_tmp: pathlib.Path
66
- :ivar _env: Environment configuration used in Purem operations, including URLs.
67
- :type _env: Any
68
- :ivar _config_url: URL for retrieving Purem configuration, either from an environment variable or a default.
69
- :type _config_url: str
70
- :ivar _loader: Loader utility for displaying loading messages during lengthy operations.
71
- :type _loader: Loader
72
- :ivar _log: Logger utility for tracking and managing logging in the Purem operations.
73
- :type _log: Logger
74
- """
75
-
76
- def __init__(self, licenced_key: Optional[str] = None):
77
- self._license_key = licenced_key or None
78
- self._lib = None
79
- self._download_binary_url = None
80
- self._ctx = ssl.create_default_context(cafile=certifi.where())
81
- self._file_structure = FileStructure()
82
- self._binary_path = self._file_structure.get_binary_path()
83
- self._binary_project_root_path = (
84
- self._file_structure.get_binary_project_root_path()
85
- )
86
- self._binary_archive_path = self._file_structure.get_binary_archive_path()
87
- self._binary_archive_path_tmp = (
88
- self._file_structure.get_binary_archive_path_tmp()
89
- )
90
- self._env = load_env_config()
91
- self._config_url = (
92
- self._env.PUREM_CONFIG_URL
93
- or "https://api.worktif.com/v2/portal/products/purem/config"
94
- )
95
- self._loader = Loader()
96
- self._log = Logger()
97
-
98
- def configure(self, license_key: Optional[str] = None) -> None:
99
- if self._license_key is None and license_key is not None:
100
- self._license_key = license_key
101
- if self._license_key is None:
102
- raise ValueError(
103
- self._log.info(
104
- "Purem requires a valid license key to initialize.\n"
105
- "You can obtain your key at https://worktif.com or through your enterprise deployment."
106
- )
107
- )
108
-
109
- self._set_binary()
110
-
111
- def softmax(self, array: ndarray) -> ndarray:
112
- shifted_arr = np.empty(array.size, dtype=np.float32)
113
- _compute_shifted_jit(array, shifted_arr)
114
- ptr = shifted_arr.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
115
- self._lib.purem(ptr, array.size)
116
- return shifted_arr
117
-
118
- def softmax_pure(self, arr, size) -> List[any]:
119
- """
120
- Compute the softmax of an array using a pure implementation.
121
-
122
- The softmax function is used to normalize an input array into a probability
123
- distribution. It is often used in machine learning for classification tasks
124
- where the output represents probabilities of different classes. This method
125
- employs a pure implementation by calling a pre-defined library function.
126
-
127
- :param arr: The input array containing numeric values to be transformed
128
- into a probability distribution.
129
- :param size: The size of the input array `arr`.
130
- :return: A list representing the normalized probability distribution obtained
131
- by applying the softmax function.
132
- """
133
- return self._lib.purem(arr, size)
134
-
135
- def _build_url(self, config: dict) -> Optional[str]:
136
- if config is None:
137
- return None
138
- base = config.get("base_url", "").rstrip("/")
139
- protocol = config.get("protocol", "https")
140
- path = config.get("pathname", "").lstrip("/")
141
- binary_url = f"{protocol}://{base}/{path}{self._license_key}"
142
- return binary_url
143
-
144
- def _tune_binary(self):
145
- self._lib = ctypes.CDLL(str(self._binary_path))
146
- self._lib.purem.argtypes = [ctypes.POINTER(ctypes.c_float), ctypes.c_size_t]
147
- self._lib.purem.restype = None
148
-
149
- def _tune_project_root_binary(self):
150
- self._lib = ctypes.CDLL(str(self._binary_project_root_path))
151
- self._lib.purem.argtypes = [ctypes.POINTER(ctypes.c_float), ctypes.c_size_t]
152
- self._lib.purem.restype = None
153
-
154
- def _load_from_latest_cdn_index(self) -> Optional[Dict]:
155
- try:
156
- if self._config_url is not None:
157
- req = urllib.request.Request(
158
- self._config_url,
159
- )
160
-
161
- with urllib.request.urlopen(req, context=self._ctx) as response:
162
- return json.load(response)
163
- else:
164
- return None
165
-
166
- except Exception:
167
- return None
168
-
169
- def _set_binary(self):
170
- if os.path.exists(self._binary_path):
171
- self._tune_binary()
172
- elif os.path.exists(self._binary_project_root_path):
173
- self._tune_project_root_binary()
174
- elif not self._license_key:
175
- raise ValueError(
176
- self._log.info(
177
- "Purem requires a valid license key to initialize.\n"
178
- "You can obtain your key at https://worktif.com or through your enterprise deployment."
179
- )
180
- )
181
- else:
182
- try:
183
- self._loader.set_message(
184
- "Initializing Purem licensed runtime locally..."
185
- )
186
- self._loader.start()
187
- self._download_binary_url = (
188
- self._build_url(self._load_from_latest_cdn_index())
189
- or f"{self._env.PUREM_DOWNLOAD_BINARY_URL}{self._license_key}"
190
- )
191
- self._download_and_extract_binary()
192
- self._loader.stop()
193
-
194
- except Exception as e:
195
- raise RuntimeError(
196
- self._log.info(
197
- "We couldn't load your Purem binary at this time.\n"
198
- "This may be a local issue or license mismatch.\n"
199
- "Please try again – or contact us at support@worktif.com.\n"
200
- "We're here to help you run at full power."
201
- )
202
- )
203
-
204
- try:
205
- self._tune_project_root_binary()
206
- except Exception as e:
207
- raise RuntimeError(
208
- self._log.info(
209
- "It appears your Purem licensed binary can not be loaded. Please try again. If the problem "
210
- "persists, please contact us at support@worktif.com. Thank you for your patience."
211
- )
212
- )
213
-
214
- def _download_and_extract_binary(self):
215
- req = urllib.request.Request(
216
- self._download_binary_url,
217
- headers={"User-Agent": "Mozilla/5.0"}
218
- )
219
-
220
- try:
221
- with urllib.request.urlopen(req, context=self._ctx) as response:
222
- with open(self._binary_archive_path_tmp, "wb") as out_file:
223
- shutil.copyfileobj(response, out_file)
224
-
225
- shutil.move(self._binary_archive_path_tmp, self._binary_archive_path)
226
- except Exception as e:
227
- raise RuntimeError(
228
- self._log.info(
229
- f"The Purem archive appears to be corrupted or incomplete.\nDetails: {e}"
230
- "Please ensure the package downloaded fully and is unmodified.\n"
231
- "Need help? Contact support@worktif.com – we'll assist right away.\n"
232
- )
233
- )
234
-
235
- try:
236
- with zipfile.ZipFile(self._binary_archive_path, "r") as zip_ref:
237
- zip_ref.extractall(
238
- self._file_structure.dirs.project_root_binary_dir_path
239
- )
240
- self._log.info_new_line(
241
- f"Purem binary extracted to: {self._file_structure.dirs.binary_dir_path}"
242
- )
243
- except zipfile.BadZipFile as e:
244
- raise RuntimeError(
245
- self._log.info(
246
- f"The Purem archive appears to be corrupted or incomplete.\nDetails: {e}"
247
- "Please ensure the package downloaded fully and is unmodified.\n"
248
- "Need help? Contact support@worktif.com – we'll assist right away.\n"
249
- )
250
- )
251
-
252
- self._binary_archive_path.unlink()
@@ -1,43 +0,0 @@
1
- """
2
- Business Source License 1.1
3
-
4
- Copyright (C) 2025 Raman Marozau, raman@worktif.com
5
- Use of this software is governed by the Business Source License included in the LICENSE.TXT file and at www.mariadb.com/bsl11.
6
-
7
- Change Date: Never
8
- On the date above, in accordance with the Business Source License, use of this software will be governed by the open source license specified in the LICENSE file.
9
- Additional Use Grant: Free for personal and non-commercial research use only.
10
-
11
- SPDX-License-Identifier: BUSL-1.1
12
- """
13
-
14
- import sys
15
- import threading
16
- import time
17
-
18
-
19
- class Loader:
20
- def __init__(self, message="Downloading"):
21
- self._message = message
22
- self._thread = threading.Thread(target=self._animate, daemon=True)
23
- self.done = False
24
-
25
- def set_message(self, message: str):
26
- self._message = message
27
-
28
- def start(self):
29
- self._thread.start()
30
-
31
- def stop(self):
32
- self.done = True
33
- self._thread.join()
34
-
35
- def _animate(self):
36
- symbols = ["|", "/", "-", "\\"]
37
- i = 0
38
- while not self.done:
39
- sys.stdout.write(f"\r{self._message}... {symbols[i % len(symbols)]}")
40
- sys.stdout.flush()
41
- i += 1
42
- time.sleep(0.1)
43
- sys.stdout.write(f"\r{self._message}... done.\n")
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes