omnata-plugin-runtime 0.5.7a130__tar.gz → 0.5.7a131__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: omnata-plugin-runtime
3
- Version: 0.5.7a130
3
+ Version: 0.5.7a131
4
4
  Summary: Classes and common runtime components for building and running Omnata Plugins
5
5
  Author: James Weakley
6
6
  Author-email: james.weakley@omnata.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "omnata-plugin-runtime"
3
- version = "0.5.7-a130"
3
+ version = "0.5.7-a131"
4
4
  description = "Classes and common runtime components for building and running Omnata Plugins"
5
5
  authors = ["James Weakley <james.weakley@omnata.com>"]
6
6
  readme = "README.md"
@@ -23,6 +23,11 @@ import threading
23
23
  import time
24
24
  import hashlib
25
25
  import requests
26
+ import pkgutil
27
+ import inspect
28
+ import importlib
29
+ import sys
30
+ import os
26
31
  from abc import ABC, abstractmethod
27
32
  from decimal import Decimal
28
33
  from functools import partial, wraps, reduce
@@ -2242,7 +2247,7 @@ def consumer_udtf(param_docs: Dict[str, Dict[str, Any]]):
2242
2247
  fields = {
2243
2248
  name: (param.annotation, param_docs.get(name, {}).get("default", param.default))
2244
2249
  for name, param in params.items()
2245
- if name != 'self' and name != 'mandatory_arg'
2250
+ if name != 'cls' and name != 'connection_parameters'
2246
2251
  }
2247
2252
 
2248
2253
  DynamicModel = create_model('DynamicModel', **fields)
@@ -2293,3 +2298,83 @@ def consumer_udtf(param_docs: Dict[str, Dict[str, Any]]):
2293
2298
 
2294
2299
  return class_decorator
2295
2300
 
2301
+ def find_classes_with_attribute(attribute_name: str,path:str = '.'):
2302
+ # Get the directory's absolute path
2303
+ current_dir = os.path.abspath(path)
2304
+
2305
+ # List to hold the classes that match the attribute
2306
+ matching_classes = []
2307
+
2308
+ # Iterate over all modules in the current directory
2309
+ for _, module_name, _ in pkgutil.iter_modules([current_dir]):
2310
+ # Import the module
2311
+ module = importlib.import_module(module_name)
2312
+
2313
+ # Iterate over all members of the module
2314
+ for name, obj in inspect.getmembers(module, inspect.isclass):
2315
+ # Check if the class has the specified attribute
2316
+ if hasattr(obj, attribute_name):
2317
+ matching_classes.append(obj)
2318
+
2319
+
2320
+ def consumer_udf(param_docs: Dict[str, Dict[str, Any]]):
2321
+ def decorator(func):
2322
+ sig = signature(func)
2323
+ params = sig.parameters
2324
+
2325
+ # Ensure the first argument is mandatory and positional
2326
+ if list(params.keys())[0] != 'connection_parameters':
2327
+ raise ValueError("The first argument should be 'connection_parameters'.")
2328
+
2329
+ # Create a Pydantic model based on the function signature and the provided documentation
2330
+ fields = {
2331
+ name: (param.annotation, param_docs.get(name, {}).get("default", param.default))
2332
+ for name, param in params.items()
2333
+ if name != 'connection_parameters'
2334
+ }
2335
+
2336
+ DynamicModel = create_model('DynamicModel', **fields)
2337
+
2338
+ # Attach the documentation to the function
2339
+ func.__doc__ = func.__doc__ or ""
2340
+ func.__doc__ += "\n\nArgs:\n"
2341
+ for name, param in params.items():
2342
+ doc = param_docs.get(name, {})
2343
+ func.__doc__ += f" {name} ({param.annotation.__name__}): {doc.get('description', 'No description provided.')}\n"
2344
+
2345
+ @wraps(func)
2346
+ def wrapper(self, connection_parameters, *args, **kwargs):
2347
+ if connection_parameters is None:
2348
+ raise ValueError("Connection not found")
2349
+
2350
+ if not isinstance(connection_parameters, Dict):
2351
+ raise ValueError("The first argument must be an object, the result of calling PLUGIN_CONNECTION.")
2352
+
2353
+ try:
2354
+ # Validate the arguments using the dynamic Pydantic model
2355
+ validated_args = DynamicModel(**kwargs)
2356
+ except ValidationError as e:
2357
+ raise ValueError(f"Argument validation error: {e}")
2358
+
2359
+ # convert the connection parameters dictionary to a ConnectionConfigurationParameters object which includes the real secrets
2360
+ if 'other_secrets_name' in connection_parameters:
2361
+ # this is the new way, where the sync engine only passes the name of the secret
2362
+ oauth_secrets_name = None
2363
+ if 'oauth_secret_name' in connection_parameters:
2364
+ oauth_secrets_name = connection_parameters['oauth_secret_name']
2365
+ del connection_parameters['oauth_secret_name']
2366
+ result = get_secrets(oauth_secrets_name,connection_parameters['other_secrets_name'])
2367
+ connection_parameters['connection_secrets'] = result
2368
+ del connection_parameters['other_secrets_name']
2369
+ parameters = ConnectionConfigurationParameters.model_validate(connection_parameters)
2370
+ else:
2371
+ # deprecated way, where the sync engine passes the secrets directly
2372
+ parameters = ConnectionConfigurationParameters.model_validate(connection_parameters)
2373
+
2374
+ # Pass the validated arguments to the function
2375
+ return func(parameters, *args, **validated_args.dict())
2376
+
2377
+ wrapper._is_omnata_consumer_udf = True
2378
+ return wrapper
2379
+
2380
+ return decorator