nebu 0.1.51__tar.gz → 0.1.52__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.
- {nebu-0.1.51/src/nebu.egg-info → nebu-0.1.52}/PKG-INFO +1 -1
- {nebu-0.1.51 → nebu-0.1.52}/pyproject.toml +1 -1
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/processors/consumer.py +7 -5
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/processors/decorate.py +115 -48
- {nebu-0.1.51 → nebu-0.1.52/src/nebu.egg-info}/PKG-INFO +1 -1
- {nebu-0.1.51 → nebu-0.1.52}/LICENSE +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/README.md +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/setup.cfg +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/__init__.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/auth.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/builders/builder.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/builders/models.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/cache.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/config.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/containers/container.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/containers/decorator.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/containers/models.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/containers/server.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/data.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/meta.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/processors/default.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/processors/models.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/processors/processor.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/processors/remote.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/redis/models.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu/services/service.py +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu.egg-info/SOURCES.txt +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu.egg-info/dependency_links.txt +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu.egg-info/requires.txt +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/src/nebu.egg-info/top_level.txt +0 -0
- {nebu-0.1.51 → nebu-0.1.52}/tests/test_containers.py +0 -0
@@ -6,7 +6,7 @@ import socket
|
|
6
6
|
import sys
|
7
7
|
import time
|
8
8
|
import traceback
|
9
|
-
import types
|
9
|
+
import types
|
10
10
|
from datetime import datetime, timezone
|
11
11
|
from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, cast
|
12
12
|
|
@@ -350,7 +350,9 @@ def process_message(message_id: str, message_data: Dict[str, str]) -> None:
|
|
350
350
|
user_id = None
|
351
351
|
try:
|
352
352
|
payload_str = message_data.get("data")
|
353
|
-
if
|
353
|
+
if (
|
354
|
+
not payload_str
|
355
|
+
): # Covers None and empty string, isinstance check is redundant
|
354
356
|
raise ValueError(
|
355
357
|
f"Missing or invalid 'data' field (expected non-empty string): {message_data}"
|
356
358
|
)
|
@@ -724,13 +726,13 @@ try:
|
|
724
726
|
assert isinstance(REDIS_STREAM, str)
|
725
727
|
assert isinstance(REDIS_CONSUMER_GROUP, str)
|
726
728
|
|
727
|
-
|
729
|
+
streams_arg: Dict[str, str] = {REDIS_STREAM: ">"}
|
730
|
+
|
728
731
|
# With decode_responses=True, redis-py expects str types here
|
729
|
-
streams_arg = {REDIS_STREAM: ">"}
|
730
732
|
messages = r.xreadgroup(
|
731
733
|
REDIS_CONSUMER_GROUP,
|
732
734
|
consumer_name,
|
733
|
-
streams_arg,
|
735
|
+
streams_arg, # type: ignore[arg-type]
|
734
736
|
count=1,
|
735
737
|
block=5000, # Use milliseconds for block
|
736
738
|
)
|
@@ -1,11 +1,7 @@
|
|
1
1
|
import ast # For parsing notebook code
|
2
2
|
import inspect
|
3
|
-
import
|
4
|
-
import os # Add os import
|
5
|
-
import re # Import re for fallback check
|
6
|
-
import tempfile # Add tempfile import
|
3
|
+
import os
|
7
4
|
import textwrap
|
8
|
-
import uuid # Add uuid import
|
9
5
|
from typing import (
|
10
6
|
Any,
|
11
7
|
Callable,
|
@@ -17,15 +13,15 @@ from typing import (
|
|
17
13
|
get_origin,
|
18
14
|
get_type_hints,
|
19
15
|
)
|
20
|
-
from urllib.parse import urlparse
|
16
|
+
from urllib.parse import urlparse
|
21
17
|
|
22
|
-
import dill
|
23
|
-
import requests
|
24
|
-
from botocore.exceptions import ClientError
|
18
|
+
import dill
|
19
|
+
import requests
|
20
|
+
from botocore.exceptions import ClientError
|
25
21
|
from pydantic import BaseModel
|
26
22
|
|
27
|
-
from nebu.auth import get_user_profile
|
28
|
-
from nebu.config import GlobalConfig
|
23
|
+
from nebu.auth import get_user_profile
|
24
|
+
from nebu.config import GlobalConfig
|
29
25
|
from nebu.containers.models import (
|
30
26
|
V1AuthzConfig,
|
31
27
|
V1ContainerHealthCheck,
|
@@ -38,7 +34,7 @@ from nebu.containers.models import (
|
|
38
34
|
V1VolumeDriver,
|
39
35
|
V1VolumePath,
|
40
36
|
)
|
41
|
-
from nebu.data import Bucket
|
37
|
+
from nebu.data import Bucket
|
42
38
|
from nebu.meta import V1ResourceMetaRequest
|
43
39
|
from nebu.processors.models import (
|
44
40
|
Message,
|
@@ -82,7 +78,7 @@ def is_jupyter_notebook():
|
|
82
78
|
import IPython # Now safe to import
|
83
79
|
|
84
80
|
ip = IPython.get_ipython()
|
85
|
-
if ip is None:
|
81
|
+
if ip is None: # type: ignore
|
86
82
|
# print("[DEBUG Helper] is_jupyter_notebook: No IPython instance found.")
|
87
83
|
return False
|
88
84
|
class_name = str(ip.__class__)
|
@@ -93,7 +89,9 @@ def is_jupyter_notebook():
|
|
93
89
|
# print("[DEBUG Helper] is_jupyter_notebook: Not Jupyter (IPython instance found, but not ZMQInteractiveShell).")
|
94
90
|
return False
|
95
91
|
except Exception as e:
|
96
|
-
|
92
|
+
print(
|
93
|
+
f"[DEBUG Helper] is_jupyter_notebook: Exception occurred: {e}"
|
94
|
+
) # Reduce verbosity
|
97
95
|
return False
|
98
96
|
|
99
97
|
|
@@ -164,9 +162,10 @@ def extract_definition_source_from_string(
|
|
164
162
|
found_in_cell = False
|
165
163
|
for node in ast.walk(tree):
|
166
164
|
if (
|
167
|
-
isinstance(
|
168
|
-
|
169
|
-
|
165
|
+
isinstance(
|
166
|
+
node, def_type
|
167
|
+
) # Check if it's the right type (FuncDef or ClassDef)
|
168
|
+
and getattr(node, "name", None) == def_name # Safely check name
|
170
169
|
):
|
171
170
|
print(
|
172
171
|
f"[DEBUG Helper] extract: Found node for '{def_name}' in cell #{cell_num}."
|
@@ -189,6 +188,8 @@ def extract_definition_source_from_string(
|
|
189
188
|
end_lineno = getattr(node, "end_lineno", start_lineno + 1)
|
190
189
|
|
191
190
|
if hasattr(node, "decorator_list") and node.decorator_list:
|
191
|
+
# Ensure it's a node type that *can* have decorators
|
192
|
+
# FunctionDef and ClassDef have decorator_list
|
192
193
|
first_decorator_start_line = (
|
193
194
|
getattr(
|
194
195
|
node.decorator_list[0], "lineno", start_lineno + 1
|
@@ -605,21 +606,38 @@ def processor(
|
|
605
606
|
# print(f"[DEBUG Decorator] Added string source to env for included obj: {obj_name_str}")
|
606
607
|
elif isinstance(obj_source, tuple):
|
607
608
|
# Handle tuple source (origin, args) - assumes get_model_source/get_type_source logic
|
608
|
-
|
609
|
-
if
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
if isinstance(arg_src, str):
|
609
|
+
# Ensure obj_source is indeed a tuple before unpacking
|
610
|
+
if len(obj_source) == 2:
|
611
|
+
# Now safe to unpack
|
612
|
+
origin_src, arg_srcs = obj_source
|
613
|
+
# type: ignore[misc] # Suppress persistent tuple unpacking error
|
614
|
+
if origin_src and isinstance(origin_src, str):
|
615
615
|
all_env.append(
|
616
616
|
V1EnvVar(
|
617
|
-
key=f"{env_key_base}
|
618
|
-
value=arg_src,
|
617
|
+
key=f"{env_key_base}_SOURCE", value=origin_src
|
619
618
|
)
|
620
619
|
)
|
621
|
-
# Handle
|
622
|
-
|
620
|
+
# Handle arg_srcs (this part seems okay)
|
621
|
+
if isinstance(arg_srcs, list):
|
622
|
+
for j, arg_src in enumerate(arg_srcs):
|
623
|
+
if isinstance(arg_src, str):
|
624
|
+
all_env.append(
|
625
|
+
V1EnvVar(
|
626
|
+
key=f"{env_key_base}_ARG_{j}_SOURCE",
|
627
|
+
value=arg_src,
|
628
|
+
)
|
629
|
+
)
|
630
|
+
else:
|
631
|
+
print(
|
632
|
+
f"[DEBUG Decorator] Warning: Expected arg_srcs to be a list, got {type(arg_srcs)}"
|
633
|
+
)
|
634
|
+
else:
|
635
|
+
# Handle unexpected type or structure for obj_source if necessary
|
636
|
+
# For now, assume it fits the expected tuple structure if isinstance passes
|
637
|
+
# origin_src, arg_srcs = None, [] # Default/error state (already covered by outer check)
|
638
|
+
print(
|
639
|
+
f"[DEBUG Decorator] Warning: Unexpected obj_source structure: {obj_source}"
|
640
|
+
)
|
623
641
|
else:
|
624
642
|
print(
|
625
643
|
f"Warning: Unknown source type for included object {obj_name_str}: {type(obj_source)}"
|
@@ -826,19 +844,44 @@ def processor(
|
|
826
844
|
# Add: Included object sources (if any)
|
827
845
|
# Add: INIT_FUNC_NAME (if provided)
|
828
846
|
|
829
|
-
#
|
830
|
-
|
847
|
+
# Calculate module_path based on relative file path
|
848
|
+
calculated_module_path = None
|
831
849
|
if rel_func_path:
|
832
850
|
# Convert OS-specific path to module path (e.g., subdir/file.py -> subdir.file)
|
833
|
-
|
834
|
-
if
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
851
|
+
base, ext = os.path.splitext(rel_func_path)
|
852
|
+
if ext == ".py":
|
853
|
+
module_path_parts = base.split(os.sep)
|
854
|
+
if module_path_parts[-1] == "__init__":
|
855
|
+
module_path_parts.pop() # Remove __init__
|
856
|
+
# Filter out potential empty strings if path started with / or had //
|
857
|
+
module_path_parts = [part for part in module_path_parts if part]
|
858
|
+
calculated_module_path = ".".join(module_path_parts)
|
859
|
+
else:
|
860
|
+
# Not a python file? Should not happen based on inspect.getfile
|
861
|
+
print(
|
862
|
+
f"[DEBUG Decorator] Warning: Function source file is not a .py file: {rel_func_path}"
|
863
|
+
)
|
864
|
+
# Set calculated_module_path to None explicitly to trigger fallback later
|
865
|
+
calculated_module_path = None
|
866
|
+
else:
|
867
|
+
# Should have errored earlier if rel_func_path is None
|
868
|
+
print(
|
869
|
+
"[DEBUG Decorator] Warning: Could not determine relative function path. Falling back to func.__module__."
|
870
|
+
)
|
871
|
+
# Set calculated_module_path to None explicitly to trigger fallback later
|
872
|
+
calculated_module_path = None
|
873
|
+
|
874
|
+
# Assign final module_path using fallback if calculation failed or wasn't applicable
|
875
|
+
if calculated_module_path is not None:
|
876
|
+
module_path = calculated_module_path
|
877
|
+
print(f"[DEBUG Decorator] Using calculated module path: {module_path}")
|
878
|
+
else:
|
879
|
+
module_path = func.__module__ # Fallback
|
880
|
+
print(f"[DEBUG Decorator] Falling back to func.__module__: {module_path}")
|
841
881
|
|
882
|
+
# Basic info needed by consumer to find and run the function
|
883
|
+
all_env.append(V1EnvVar(key="FUNCTION_NAME", value=processor_name))
|
884
|
+
if rel_func_path:
|
842
885
|
# For now, just pass the relative file path, consumer will handle conversion
|
843
886
|
all_env.append(
|
844
887
|
V1EnvVar(key="NEBU_ENTRYPOINT_MODULE_PATH", value=rel_func_path)
|
@@ -846,9 +889,7 @@ def processor(
|
|
846
889
|
print(
|
847
890
|
f"[DEBUG Decorator] Set NEBU_ENTRYPOINT_MODULE_PATH to: {rel_func_path}"
|
848
891
|
)
|
849
|
-
else
|
850
|
-
# Should have errored earlier if rel_func_path is None
|
851
|
-
raise RuntimeError("Internal error: Relative function path not determined.")
|
892
|
+
# No else needed, handled by fallback calculation above
|
852
893
|
|
853
894
|
if init_func:
|
854
895
|
init_func_name = init_func.__name__ # Get name here
|
@@ -862,26 +903,52 @@ def processor(
|
|
862
903
|
print(f"[DEBUG Decorator] Set INIT_FUNC_NAME to: {init_func_name}")
|
863
904
|
|
864
905
|
# Type info (still useful for deserialization/validation in consumer)
|
906
|
+
# Adjust type strings to replace '__main__' with the calculated module path
|
907
|
+
param_type_str_repr = str(param_type)
|
908
|
+
if module_path != "__main__" and "__main__." in param_type_str_repr:
|
909
|
+
# Be careful with replacement - replace only module prefix
|
910
|
+
# Example: "<class '__main__.MyModel'>" -> "<class 'mymodule.MyModel'>"
|
911
|
+
# Example: "typing.Optional[__main__.MyModel]" -> "typing.Optional[mymodule.MyModel]"
|
912
|
+
param_type_str_repr = param_type_str_repr.replace(
|
913
|
+
"__main__.", f"{module_path}."
|
914
|
+
)
|
915
|
+
print(
|
916
|
+
f"[DEBUG Decorator] Adjusted param type string: {param_type_str_repr}"
|
917
|
+
)
|
918
|
+
|
865
919
|
all_env.append(V1EnvVar(key="PARAM_TYPE_STR", value=param_type_str_repr))
|
866
|
-
|
867
|
-
|
868
|
-
|
920
|
+
|
921
|
+
return_type_str_repr = str(return_type)
|
922
|
+
if module_path != "__main__" and "__main__." in return_type_str_repr:
|
923
|
+
return_type_str_repr = return_type_str_repr.replace(
|
924
|
+
"__main__.", f"{module_path}."
|
925
|
+
)
|
926
|
+
print(
|
927
|
+
f"[DEBUG Decorator] Adjusted return type string: {return_type_str_repr}"
|
928
|
+
)
|
929
|
+
|
930
|
+
all_env.append(V1EnvVar(key="RETURN_TYPE_STR", value=return_type_str_repr))
|
869
931
|
all_env.append(V1EnvVar(key="IS_STREAM_MESSAGE", value=str(is_stream_message)))
|
870
932
|
if content_type and hasattr(content_type, "__name__"):
|
933
|
+
content_type_name = None
|
871
934
|
# Check if content_type is a class before accessing __name__
|
872
935
|
if isinstance(content_type, type):
|
936
|
+
content_type_name = content_type.__name__
|
873
937
|
all_env.append(
|
874
|
-
V1EnvVar(key="CONTENT_TYPE_NAME", value=
|
938
|
+
V1EnvVar(key="CONTENT_TYPE_NAME", value=content_type_name)
|
875
939
|
)
|
876
940
|
else:
|
877
941
|
# Handle unresolved types / typevars if needed
|
878
942
|
print(
|
879
943
|
f"Warning: Content type '{content_type}' is not a class, cannot get name."
|
880
944
|
)
|
881
|
-
#
|
945
|
+
# Use the calculated module_path for MODULE_NAME
|
882
946
|
all_env.append(
|
883
|
-
V1EnvVar(
|
884
|
-
|
947
|
+
V1EnvVar(
|
948
|
+
key="MODULE_NAME", value=module_path
|
949
|
+
) # module_path is guaranteed to be a string here (calculated or fallback)
|
950
|
+
)
|
951
|
+
print(f"[DEBUG Decorator] Set MODULE_NAME to: {module_path}")
|
885
952
|
|
886
953
|
# Add PYTHONPATH
|
887
954
|
pythonpath_value = CONTAINER_CODE_DIR
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|