ob-metaflow 2.11.4.9__py2.py3-none-any.whl → 2.11.9.1__py2.py3-none-any.whl

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 ob-metaflow might be problematic. Click here for more details.

Files changed (38) hide show
  1. metaflow/cli.py +15 -10
  2. metaflow/clone_util.py +71 -0
  3. metaflow/cmd/develop/stub_generator.py +2 -0
  4. metaflow/cmd/develop/stubs.py +17 -8
  5. metaflow/metaflow_config.py +3 -0
  6. metaflow/package.py +4 -3
  7. metaflow/parameters.py +2 -2
  8. metaflow/plugins/aws/batch/batch.py +12 -0
  9. metaflow/plugins/aws/batch/batch_cli.py +25 -0
  10. metaflow/plugins/aws/batch/batch_client.py +40 -0
  11. metaflow/plugins/aws/batch/batch_decorator.py +32 -1
  12. metaflow/plugins/aws/step_functions/step_functions.py +3 -0
  13. metaflow/plugins/datatools/s3/s3op.py +4 -3
  14. metaflow/plugins/env_escape/client.py +154 -27
  15. metaflow/plugins/env_escape/client_modules.py +15 -47
  16. metaflow/plugins/env_escape/configurations/emulate_test_lib/overrides.py +31 -42
  17. metaflow/plugins/env_escape/configurations/emulate_test_lib/server_mappings.py +8 -3
  18. metaflow/plugins/env_escape/configurations/test_lib_impl/test_lib.py +74 -22
  19. metaflow/plugins/env_escape/consts.py +1 -0
  20. metaflow/plugins/env_escape/exception_transferer.py +46 -112
  21. metaflow/plugins/env_escape/override_decorators.py +8 -8
  22. metaflow/plugins/env_escape/server.py +42 -5
  23. metaflow/plugins/env_escape/stub.py +168 -23
  24. metaflow/plugins/env_escape/utils.py +3 -3
  25. metaflow/plugins/gcp/gcp_secret_manager_secrets_provider.py +3 -2
  26. metaflow/plugins/pypi/conda_environment.py +9 -0
  27. metaflow/plugins/pypi/pip.py +17 -2
  28. metaflow/runtime.py +252 -61
  29. metaflow/sidecar/sidecar.py +11 -1
  30. metaflow/sidecar/sidecar_subprocess.py +34 -18
  31. metaflow/task.py +28 -54
  32. metaflow/version.py +1 -1
  33. {ob_metaflow-2.11.4.9.dist-info → ob_metaflow-2.11.9.1.dist-info}/METADATA +2 -2
  34. {ob_metaflow-2.11.4.9.dist-info → ob_metaflow-2.11.9.1.dist-info}/RECORD +38 -37
  35. {ob_metaflow-2.11.4.9.dist-info → ob_metaflow-2.11.9.1.dist-info}/WHEEL +1 -1
  36. {ob_metaflow-2.11.4.9.dist-info → ob_metaflow-2.11.9.1.dist-info}/LICENSE +0 -0
  37. {ob_metaflow-2.11.4.9.dist-info → ob_metaflow-2.11.9.1.dist-info}/entry_points.txt +0 -0
  38. {ob_metaflow-2.11.4.9.dist-info → ob_metaflow-2.11.9.1.dist-info}/top_level.txt +0 -0
@@ -34,8 +34,12 @@ from .communication.channel import Channel
34
34
  from .communication.socket_bytestream import SocketByteStream
35
35
 
36
36
  from .data_transferer import DataTransferer, ObjReference
37
- from .exception_transferer import load_exception
38
- from .override_decorators import LocalAttrOverride, LocalException, LocalOverride
37
+ from .exception_transferer import ExceptionMetaClass, load_exception
38
+ from .override_decorators import (
39
+ LocalAttrOverride,
40
+ LocalExceptionDeserializer,
41
+ LocalOverride,
42
+ )
39
43
  from .stub import create_class
40
44
  from .utils import get_canonical_name
41
45
 
@@ -193,28 +197,41 @@ class Client(object):
193
197
  self._proxied_classes = {
194
198
  k: None
195
199
  for k in itertools.chain(
196
- response[FIELD_CONTENT]["classes"], response[FIELD_CONTENT]["proxied"]
200
+ response[FIELD_CONTENT]["classes"],
201
+ response[FIELD_CONTENT]["proxied"],
202
+ (e[0] for e in response[FIELD_CONTENT]["exceptions"]),
197
203
  )
198
204
  }
199
205
 
206
+ self._exception_hierarchy = dict(response[FIELD_CONTENT]["exceptions"])
207
+ self._proxied_classnames = set(response[FIELD_CONTENT]["classes"]).union(
208
+ response[FIELD_CONTENT]["proxied"]
209
+ )
210
+ self._aliases = response[FIELD_CONTENT]["aliases"]
211
+
200
212
  # Determine all overrides
201
213
  self._overrides = {}
202
214
  self._getattr_overrides = {}
203
215
  self._setattr_overrides = {}
204
- self._exception_overrides = {}
216
+ self._exception_deserializers = {}
205
217
  for override in override_values:
206
218
  if isinstance(override, (LocalOverride, LocalAttrOverride)):
207
219
  for obj_name, obj_funcs in override.obj_mapping.items():
208
- if obj_name not in self._proxied_classes:
220
+ canonical_name = get_canonical_name(obj_name, self._aliases)
221
+ if canonical_name not in self._proxied_classes:
209
222
  raise ValueError(
210
223
  "%s does not refer to a proxied or override type" % obj_name
211
224
  )
212
225
  if isinstance(override, LocalOverride):
213
- override_dict = self._overrides.setdefault(obj_name, {})
226
+ override_dict = self._overrides.setdefault(canonical_name, {})
214
227
  elif override.is_setattr:
215
- override_dict = self._setattr_overrides.setdefault(obj_name, {})
228
+ override_dict = self._setattr_overrides.setdefault(
229
+ canonical_name, {}
230
+ )
216
231
  else:
217
- override_dict = self._getattr_overrides.setdefault(obj_name, {})
232
+ override_dict = self._getattr_overrides.setdefault(
233
+ canonical_name, {}
234
+ )
218
235
  if isinstance(obj_funcs, str):
219
236
  obj_funcs = (obj_funcs,)
220
237
  for name in obj_funcs:
@@ -223,11 +240,18 @@ class Client(object):
223
240
  "%s was already overridden for %s" % (name, obj_name)
224
241
  )
225
242
  override_dict[name] = override.func
226
- if isinstance(override, LocalException):
227
- cur_ex = self._exception_overrides.get(override.class_path, None)
228
- if cur_ex is not None:
229
- raise ValueError("Exception %s redefined" % override.class_path)
230
- self._exception_overrides[override.class_path] = override.wrapped_class
243
+ if isinstance(override, LocalExceptionDeserializer):
244
+ canonical_name = get_canonical_name(override.class_path, self._aliases)
245
+ if canonical_name not in self._exception_hierarchy:
246
+ raise ValueError(
247
+ "%s does not refer to an exception type" % override.class_path
248
+ )
249
+ cur_des = self._exception_deserializers.get(canonical_name, None)
250
+ if cur_des is not None:
251
+ raise ValueError(
252
+ "Exception %s has multiple deserializers" % override.class_path
253
+ )
254
+ self._exception_deserializers[canonical_name] = override.deserializer
231
255
 
232
256
  # Proxied standalone functions are functions that are proxied
233
257
  # as part of other objects like defaultdict for which we create a
@@ -243,8 +267,6 @@ class Client(object):
243
267
  "aliases": response[FIELD_CONTENT]["aliases"],
244
268
  }
245
269
 
246
- self._aliases = response[FIELD_CONTENT]["aliases"]
247
-
248
270
  def __del__(self):
249
271
  self.cleanup()
250
272
 
@@ -288,8 +310,9 @@ class Client(object):
288
310
  def get_exports(self):
289
311
  return self._export_info
290
312
 
291
- def get_local_exception_overrides(self):
292
- return self._exception_overrides
313
+ def get_exception_deserializer(self, name):
314
+ cannonical_name = get_canonical_name(name, self._aliases)
315
+ return self._exception_deserializers.get(cannonical_name)
293
316
 
294
317
  def stub_request(self, stub, request_type, *args, **kwargs):
295
318
  # Encode the operation to send over the wire and wait for the response
@@ -313,7 +336,7 @@ class Client(object):
313
336
  if response_type == MSG_REPLY:
314
337
  return self.decode(response[FIELD_CONTENT])
315
338
  elif response_type == MSG_EXCEPTION:
316
- raise load_exception(self._datatransferer, response[FIELD_CONTENT])
339
+ raise load_exception(self, response[FIELD_CONTENT])
317
340
  elif response_type == MSG_INTERNAL_ERROR:
318
341
  raise RuntimeError(
319
342
  "Error in the server runtime:\n\n===== SERVER TRACEBACK =====\n%s"
@@ -334,10 +357,27 @@ class Client(object):
334
357
  # this connection will be converted to a local stub.
335
358
  return self._datatransferer.load(json_obj)
336
359
 
337
- def get_local_class(self, name, obj_id=None):
360
+ def get_local_class(
361
+ self, name, obj_id=None, is_returned_exception=False, is_parent=False
362
+ ):
338
363
  # Gets (and creates if needed), the class mapping to the remote
339
364
  # class of name 'name'.
365
+
366
+ # We actually deal with four types of classes:
367
+ # - proxied functions
368
+ # - classes that are proxied regular classes AND proxied exceptions
369
+ # - classes that are proxied regular classes AND NOT proxied exceptions
370
+ # - classes that are NOT proxied regular classes AND are proxied exceptions
340
371
  name = get_canonical_name(name, self._aliases)
372
+
373
+ def name_to_parent_name(name):
374
+ return "parent:%s" % name
375
+
376
+ if is_parent:
377
+ lookup_name = name_to_parent_name(name)
378
+ else:
379
+ lookup_name = name
380
+
341
381
  if name == "function":
342
382
  # Special handling of pickled functions. We create a new class that
343
383
  # simply has a __call__ method that will forward things back to
@@ -346,17 +386,87 @@ class Client(object):
346
386
  raise RuntimeError("Local function unpickling without an object ID")
347
387
  if obj_id not in self._proxied_standalone_functions:
348
388
  self._proxied_standalone_functions[obj_id] = create_class(
349
- self, "__function_%s" % obj_id, {}, {}, {}, {"__call__": ""}
389
+ self,
390
+ "__main__.__function_%s" % obj_id,
391
+ {},
392
+ {},
393
+ {},
394
+ {"__call__": ""},
395
+ [],
350
396
  )
351
397
  return self._proxied_standalone_functions[obj_id]
398
+ local_class = self._proxied_classes.get(lookup_name, None)
399
+ if local_class is not None:
400
+ return local_class
401
+
402
+ is_proxied_exception = name in self._exception_hierarchy
403
+ is_proxied_non_exception = name in self._proxied_classnames
404
+
405
+ if not is_proxied_exception and not is_proxied_non_exception:
406
+ if is_returned_exception or is_parent:
407
+ # In this case, it may be a local exception that we need to
408
+ # recreate
409
+ try:
410
+ ex_module, ex_name = name.rsplit(".", 1)
411
+ __import__(ex_module, None, None, "*")
412
+ except Exception:
413
+ pass
414
+ if ex_module in sys.modules and issubclass(
415
+ getattr(sys.modules[ex_module], ex_name), BaseException
416
+ ):
417
+ # This is a local exception that we can recreate
418
+ local_exception = getattr(sys.modules[ex_module], ex_name)
419
+ wrapped_exception = ExceptionMetaClass(
420
+ ex_name,
421
+ (local_exception,),
422
+ dict(getattr(local_exception, "__dict__", {})),
423
+ )
424
+ wrapped_exception.__module__ = ex_module
425
+ self._proxied_classes[lookup_name] = wrapped_exception
426
+ return wrapped_exception
352
427
 
353
- if name not in self._proxied_classes:
354
428
  raise ValueError("Class '%s' is not known" % name)
355
- local_class = self._proxied_classes[name]
356
- if local_class is None:
357
- # We need to build up this class. To do so, we take everything that the
358
- # remote class has and remove UNSUPPORTED things and overridden things
429
+
430
+ # At this stage:
431
+ # - we don't have a local_class for this
432
+ # - it is not an inbuilt exception so it is either a proxied exception, a
433
+ # proxied class or a proxied object that is both an exception and a class.
434
+
435
+ parents = []
436
+ if is_proxied_exception:
437
+ # If exception, we need to get the parents from the exception
438
+ ex_parents = self._exception_hierarchy[name]
439
+ for parent in ex_parents:
440
+ # We always consider it to be an exception so that we wrap even non
441
+ # proxied builtins exceptions
442
+ parents.append(self.get_local_class(parent, is_parent=True))
443
+ # For regular classes, we get what it exposes from the server
444
+ if is_proxied_non_exception:
359
445
  remote_methods = self.stub_request(None, OP_GETMETHODS, name)
446
+ else:
447
+ remote_methods = {}
448
+
449
+ parent_local_class = None
450
+ local_class = None
451
+ if is_proxied_exception:
452
+ # If we are a proxied exception AND a proxied class, we create two classes:
453
+ # actually:
454
+ # - the class itself (which is a stub)
455
+ # - the class in the capacity of a parent class (to another exception
456
+ # presumably). The reason for this is that if we have an exception/proxied
457
+ # class A and another B and B inherits from A, the MRO order would be all
458
+ # wrong since both A and B would also inherit from `Stub`. Here what we
459
+ # do is:
460
+ # - A_parent inherits from the actual parents of A (let's assume a
461
+ # builtin exception)
462
+ # - A inherits from (Stub, A_parent)
463
+ # - B_parent inherits from A_parent and the builtin Exception
464
+ # - B inherits from (Stub, B_parent)
465
+ ex_module, ex_name = name.rsplit(".", 1)
466
+ parent_local_class = ExceptionMetaClass(ex_name, (*parents,), {})
467
+ parent_local_class.__module__ = ex_module
468
+
469
+ if is_proxied_non_exception:
360
470
  local_class = create_class(
361
471
  self,
362
472
  name,
@@ -364,9 +474,26 @@ class Client(object):
364
474
  self._getattr_overrides.get(name, {}),
365
475
  self._setattr_overrides.get(name, {}),
366
476
  remote_methods,
477
+ (parent_local_class,) if parent_local_class else None,
367
478
  )
479
+ if parent_local_class:
480
+ self._proxied_classes[name_to_parent_name(name)] = parent_local_class
481
+ if local_class:
368
482
  self._proxied_classes[name] = local_class
369
- return local_class
483
+ else:
484
+ # This is for the case of pure proxied exceptions -- we want the lookup of
485
+ # foo.MyException to be the same class as looking of foo.MyException as a parent
486
+ # of another exception so `isinstance` works properly
487
+ self._proxied_classes[name] = parent_local_class
488
+
489
+ if is_parent:
490
+ # This should never happen but making sure
491
+ if not parent_local_class:
492
+ raise RuntimeError(
493
+ "Exception parent class %s is not a proxied exception" % name
494
+ )
495
+ return parent_local_class
496
+ return self._proxied_classes[name]
370
497
 
371
498
  def can_pickle(self, obj):
372
499
  return getattr(obj, "___connection___", None) == self
@@ -395,7 +522,7 @@ class Client(object):
395
522
  obj_id = obj.identifier
396
523
  local_instance = self._proxied_objects.get(obj_id)
397
524
  if not local_instance:
398
- local_class = self.get_local_class(remote_class_name, obj_id)
525
+ local_class = self.get_local_class(remote_class_name, obj_id=obj_id)
399
526
  local_instance = local_class(self, remote_class_name, obj_id)
400
527
  return local_instance
401
528
 
@@ -7,7 +7,6 @@ import sys
7
7
 
8
8
  from .consts import OP_CALLFUNC, OP_GETVAL, OP_SETVAL
9
9
  from .client import Client
10
- from .override_decorators import LocalException
11
10
  from .utils import get_canonical_name
12
11
 
13
12
 
@@ -16,7 +15,7 @@ def _clean_client(client):
16
15
 
17
16
 
18
17
  class _WrappedModule(object):
19
- def __init__(self, loader, prefix, exports, exception_classes, client):
18
+ def __init__(self, loader, prefix, exports, client):
20
19
  self._loader = loader
21
20
  self._prefix = prefix
22
21
  self._client = client
@@ -24,19 +23,20 @@ class _WrappedModule(object):
24
23
  r"^%s\.([a-zA-Z_][a-zA-Z0-9_]*)$" % prefix.replace(".", r"\.") # noqa W605
25
24
  )
26
25
  self._exports = {}
27
- self._aliases = exports["aliases"]
26
+ self._aliases = exports.get("aliases", [])
28
27
  for k in ("classes", "functions", "values"):
29
28
  result = []
30
- for item in exports[k]:
29
+ for item in exports.get(k, []):
31
30
  m = is_match.match(item)
32
31
  if m:
33
32
  result.append(m.group(1))
34
33
  self._exports[k] = result
35
- self._exception_classes = {}
36
- for k, v in exception_classes.items():
37
- m = is_match.match(k)
34
+ result = []
35
+ for item, _ in exports.get("exceptions", []):
36
+ m = is_match.match(item)
38
37
  if m:
39
- self._exception_classes[m.group(1)] = v
38
+ result.append(m.group(1))
39
+ self._exports["exceptions"] = result
40
40
 
41
41
  def __getattr__(self, name):
42
42
  if name == "__loader__":
@@ -50,8 +50,8 @@ class _WrappedModule(object):
50
50
  name = get_canonical_name(self._prefix + "." + name, self._aliases)[
51
51
  len(self._prefix) + 1 :
52
52
  ]
53
- if name in self._exports["classes"]:
54
- # We load classes lazily
53
+ if name in self._exports["classes"] or name in self._exports["exceptions"]:
54
+ # We load classes and exceptions lazily
55
55
  return self._client.get_local_class("%s.%s" % (self._prefix, name))
56
56
  elif name in self._exports["functions"]:
57
57
  # TODO: Grab doc back from the remote side like in _make_method
@@ -67,8 +67,6 @@ class _WrappedModule(object):
67
67
  return self._client.stub_request(
68
68
  None, OP_GETVAL, "%s.%s" % (self._prefix, name)
69
69
  )
70
- elif name in self._exception_classes:
71
- return self._exception_classes[name]
72
70
  else:
73
71
  # Try to see if this is a submodule that we can load
74
72
  m = None
@@ -173,7 +171,6 @@ class ModuleImporter(object):
173
171
 
174
172
  # Get information about overrides and what the server knows about
175
173
  exports = self._client.get_exports()
176
- ex_overrides = self._client.get_local_exception_overrides()
177
174
 
178
175
  prefixes = set()
179
176
  export_classes = exports.get("classes", [])
@@ -182,42 +179,13 @@ class ModuleImporter(object):
182
179
  export_exceptions = exports.get("exceptions", [])
183
180
  self._aliases = exports.get("aliases", {})
184
181
  for name in itertools.chain(
185
- export_classes, export_functions, export_values
182
+ export_classes,
183
+ export_functions,
184
+ export_values,
185
+ (e[0] for e in export_exceptions),
186
186
  ):
187
187
  splits = name.rsplit(".", 1)
188
188
  prefixes.add(splits[0])
189
-
190
- # Now look at the exceptions coming from the server
191
- formed_exception_classes = {}
192
- for ex_name, ex_parents in export_exceptions:
193
- # Exception is a tuple (name, (parents,))
194
- # Exceptions are also given in order of instantiation (ie: the
195
- # server already topologically sorted them)
196
- ex_class_dict = ex_overrides.get(ex_name, None)
197
- if ex_class_dict is None:
198
- ex_class_dict = {}
199
- else:
200
- ex_class_dict = dict(ex_class_dict.__dict__)
201
- parents = []
202
- for fake_base in ex_parents:
203
- if fake_base.startswith("builtins."):
204
- # This is something we know of here
205
- parents.append(eval(fake_base[9:]))
206
- else:
207
- # It's in formed_classes
208
- parents.append(formed_exception_classes[fake_base])
209
- splits = ex_name.rsplit(".", 1)
210
- ex_class_dict["__user_defined__"] = set(ex_class_dict.keys())
211
- new_class = type(splits[1], tuple(parents), ex_class_dict)
212
- new_class.__module__ = splits[0]
213
- new_class.__name__ = splits[1]
214
- formed_exception_classes[ex_name] = new_class
215
-
216
- # Now update prefixes as needed
217
- for name in formed_exception_classes:
218
- splits = name.rsplit(".", 1)
219
- prefixes.add(splits[0])
220
-
221
189
  # We will make sure that we create modules even for "empty" prefixes
222
190
  # because packages are always loaded hierarchically so if we have
223
191
  # something in `a.b.c` but nothing directly in `a`, we still need to
@@ -235,7 +203,7 @@ class ModuleImporter(object):
235
203
  self._handled_modules = {}
236
204
  for prefix in prefixes:
237
205
  self._handled_modules[prefix] = _WrappedModule(
238
- self, prefix, exports, formed_exception_classes, self._client
206
+ self, prefix, exports, self._client
239
207
  )
240
208
  canonical_fullname = get_canonical_name(fullname, self._aliases)
241
209
  # Modules are created canonically but we need to return something for any
@@ -5,56 +5,44 @@ from metaflow.plugins.env_escape.override_decorators import (
5
5
  remote_override,
6
6
  remote_getattr_override,
7
7
  remote_setattr_override,
8
- local_exception,
8
+ local_exception_deserialize,
9
9
  remote_exception_serialize,
10
10
  )
11
11
 
12
12
 
13
13
  @local_override({"test_lib.TestClass1": "print_value"})
14
14
  def local_print_value(stub, func):
15
- print("Encoding before sending to server")
16
15
  v = func()
17
- print("Adding 5")
18
16
  return v + 5
19
17
 
20
18
 
21
19
  @remote_override({"test_lib.TestClass1": "print_value"})
22
20
  def remote_print_value(obj, func):
23
- print("Decoding from client")
24
21
  v = func()
25
- print("Encoding for client")
26
- return v
22
+ return v + 3
27
23
 
28
24
 
29
25
  @local_getattr_override({"test_lib.TestClass1": "override_value"})
30
26
  def local_get_value2(stub, name, func):
31
- print("In local getattr override for %s" % name)
32
27
  r = func()
33
- print("In local getattr override, got %s" % r)
34
- return r
35
-
36
-
37
- @local_setattr_override({"test_lib.TestClass1": "override_value"})
38
- def local_set_value2(stub, name, func, v):
39
- print("In local setattr override for %s" % name)
40
- r = func(v)
41
- print("In local setattr override, got %s" % r)
42
- return r
28
+ return r + 5
43
29
 
44
30
 
45
31
  @remote_getattr_override({"test_lib.TestClass1": "override_value"})
46
32
  def remote_get_value2(obj, name):
47
- print("In remote getattr override for %s" % name)
48
33
  r = getattr(obj, name)
49
- print("In remote getattr override, got %s" % r)
34
+ return r + 3
35
+
36
+
37
+ @local_setattr_override({"test_lib.TestClass1": "override_value"})
38
+ def local_set_value2(stub, name, func, v):
39
+ r = func(v + 5)
50
40
  return r
51
41
 
52
42
 
53
43
  @remote_setattr_override({"test_lib.TestClass1": "override_value"})
54
44
  def remote_set_value2(obj, name, v):
55
- print("In remote setattr override for %s" % name)
56
- r = setattr(obj, name, v)
57
- print("In remote setattr override, got %s" % r)
45
+ r = setattr(obj, name, v + 3)
58
46
  return r
59
47
 
60
48
 
@@ -63,30 +51,31 @@ def unsupported_method(stub, func, *args, **kwargs):
63
51
  return NotImplementedError("Just because")
64
52
 
65
53
 
66
- @local_override({"test_lib.package.TestClass3": "thirdfunction"})
67
- def iamthelocalthird(stub, func, val):
68
- print("Locally the Third")
69
- v = func(val)
70
- return v
54
+ @local_exception_deserialize("test_lib.SomeException")
55
+ def some_exception_deserialize(ex, json_obj):
56
+ ex.user_value = json_obj
71
57
 
72
58
 
73
- @remote_override({"test_lib.package.TestClass3": "thirdfunction"})
74
- def iamtheremotethird(obj, func, val):
75
- print("Remotely the Third")
76
- v = func(val)
77
- return v
59
+ @remote_exception_serialize("test_lib.SomeException")
60
+ def some_exception_serialize(ex):
61
+ return 42
78
62
 
79
63
 
80
- @local_exception("test_lib.SomeException")
81
- class SomeException:
82
- def __str__(self):
83
- parent_val = super(self.__realclass__, self).__str__()
84
- return parent_val + " In SomeException str override: %s" % self.user_value
64
+ @local_exception_deserialize("test_lib.ExceptionAndClass")
65
+ def exception_and_class_deserialize(ex, json_obj):
66
+ ex.user_value = json_obj
85
67
 
86
- def _deserialize_user(self, json_obj):
87
- self.user_value = json_obj
88
68
 
69
+ @remote_exception_serialize("test_lib.ExceptionAndClass")
70
+ def exception_and_class_serialize(ex):
71
+ return 43
89
72
 
90
- @remote_exception_serialize("test_lib.SomeException")
91
- def some_exception_serialize(ex):
92
- return 42
73
+
74
+ @local_exception_deserialize("test_lib.ExceptionAndClassChild")
75
+ def exception_and_class_child_deserialize(ex, json_obj):
76
+ ex.user_value = json_obj
77
+
78
+
79
+ @remote_exception_serialize("test_lib.ExceptionAndClassChild")
80
+ def exception_and_class_child_serialize(ex):
81
+ return 44
@@ -10,17 +10,22 @@ sys.path.append(
10
10
  import test_lib as lib
11
11
 
12
12
  EXPORTED_CLASSES = {
13
- "test_lib": {
13
+ ("test_lib", "test_lib.alias"): {
14
14
  "TestClass1": lib.TestClass1,
15
15
  "TestClass2": lib.TestClass2,
16
- "package.TestClass3": lib.TestClass3,
16
+ "BaseClass": lib.BaseClass,
17
+ "ChildClass": lib.ChildClass,
18
+ "ExceptionAndClass": lib.ExceptionAndClass,
19
+ "ExceptionAndClassChild": lib.ExceptionAndClassChild,
17
20
  }
18
21
  }
19
22
 
20
23
  EXPORTED_EXCEPTIONS = {
21
- "test_lib": {
24
+ ("test_lib", "test_lib.alias"): {
22
25
  "SomeException": lib.SomeException,
23
26
  "MyBaseException": lib.MyBaseException,
27
+ "ExceptionAndClass": lib.ExceptionAndClass,
28
+ "ExceptionAndClassChild": lib.ExceptionAndClassChild,
24
29
  }
25
30
  }
26
31
 
@@ -1,4 +1,5 @@
1
1
  import functools
2
+ from html.parser import HTMLParser
2
3
 
3
4
 
4
5
  class MyBaseException(Exception):
@@ -9,8 +10,48 @@ class SomeException(MyBaseException):
9
10
  pass
10
11
 
11
12
 
12
- class TestClass1(object):
13
+ class ExceptionAndClass(MyBaseException):
14
+ def __init__(self, *args):
15
+ super().__init__(*args)
16
+
17
+ def method_on_exception(self):
18
+ return "method_on_exception"
19
+
20
+ def __str__(self):
21
+ return "ExceptionAndClass Str: %s" % super().__str__()
22
+
23
+
24
+ class ExceptionAndClassChild(ExceptionAndClass):
25
+ def __init__(self, *args):
26
+ super().__init__(*args)
27
+
28
+ def method_on_child_exception(self):
29
+ return "method_on_child_exception"
30
+
31
+ def __str__(self):
32
+ return "ExceptionAndClassChild Str: %s" % super().__str__()
33
+
13
34
 
35
+ class BaseClass(HTMLParser):
36
+ def __init__(self, *args, **kwargs):
37
+ super().__init__(*args, **kwargs)
38
+ self._output = []
39
+
40
+ def handle_starttag(self, tag, attrs):
41
+ self._output.append(tag)
42
+ return super().handle_starttag(tag, attrs)
43
+
44
+ def get_output(self):
45
+ return self._output
46
+
47
+
48
+ class ChildClass(BaseClass):
49
+ def handle_endtag(self, tag):
50
+ self._output.append(tag)
51
+ return super().handle_endtag(tag)
52
+
53
+
54
+ class TestClass1(object):
14
55
  cls_object = 25
15
56
 
16
57
  def __init__(self, value):
@@ -41,11 +82,11 @@ class TestClass1(object):
41
82
  return TestClass2(self._value, stride, count)
42
83
 
43
84
  @staticmethod
44
- def somethingstatic(val):
85
+ def static_method(val):
45
86
  return val + 42
46
87
 
47
88
  @classmethod
48
- def somethingclass(cls):
89
+ def class_method(cls):
49
90
  return cls.cls_object
50
91
 
51
92
  @property
@@ -56,13 +97,42 @@ class TestClass1(object):
56
97
  def override_value(self, value):
57
98
  self._value2 = value
58
99
 
100
+ def __hidden(self, name, value):
101
+ setattr(self, name, value)
102
+
103
+ def weird_indirection(self, name):
104
+ return functools.partial(self.__hidden, name)
105
+
106
+ def returnChild(self):
107
+ return ChildClass()
108
+
109
+ def raiseOrReturnValueError(self, doRaise=False):
110
+ if doRaise:
111
+ raise ValueError("I raised")
112
+ return ValueError("I returned")
113
+
114
+ def raiseOrReturnSomeException(self, doRaise=False):
115
+ if doRaise:
116
+ raise SomeException("I raised")
117
+ return SomeException("I returned")
118
+
119
+ def raiseOrReturnExceptionAndClass(self, doRaise=False):
120
+ if doRaise:
121
+ raise ExceptionAndClass("I raised")
122
+ return ExceptionAndClass("I returned")
123
+
124
+ def raiseOrReturnExceptionAndClassChild(self, doRaise=False):
125
+ if doRaise:
126
+ raise ExceptionAndClassChild("I raised")
127
+ return ExceptionAndClassChild("I returned")
128
+
59
129
 
60
130
  class TestClass2(object):
61
131
  def __init__(self, value, stride, count):
62
132
  self._mylist = [value + stride * i for i in range(count)]
63
133
 
64
134
  def something(self, val):
65
- return "In Test2 with %s" % val
135
+ return "Test2:Something:%s" % val
66
136
 
67
137
  def __iter__(self):
68
138
  self._pos = 0
@@ -75,24 +145,6 @@ class TestClass2(object):
75
145
  raise StopIteration
76
146
 
77
147
 
78
- class TestClass3(object):
79
- def __init__(self):
80
- print("I am Class3")
81
-
82
- def thirdfunction(self, val):
83
- print("Got value: %s" % val)
84
- # raise AttributeError("Some weird error")
85
-
86
- def raiseSomething(self):
87
- raise SomeException("Something went wrong")
88
-
89
- def __hidden(self, name, value):
90
- setattr(self, name, value)
91
-
92
- def weird_indirection(self, name):
93
- return functools.partial(self.__hidden, name)
94
-
95
-
96
148
  def test_func(*args, **kwargs):
97
149
  return "In test func"
98
150