nebu 0.1.88__py3-none-any.whl → 0.1.91__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.
- nebu/containers/container.py +6 -3
- nebu/namespaces/namespace.py +8 -4
- nebu/processors/consumer.py +69 -49
- nebu/processors/decorate.py +23 -0
- nebu/processors/processor.py +9 -4
- {nebu-0.1.88.dist-info → nebu-0.1.91.dist-info}/METADATA +1 -1
- {nebu-0.1.88.dist-info → nebu-0.1.91.dist-info}/RECORD +10 -10
- {nebu-0.1.88.dist-info → nebu-0.1.91.dist-info}/WHEEL +0 -0
- {nebu-0.1.88.dist-info → nebu-0.1.91.dist-info}/licenses/LICENSE +0 -0
- {nebu-0.1.88.dist-info → nebu-0.1.91.dist-info}/top_level.txt +0 -0
nebu/containers/container.py
CHANGED
@@ -42,13 +42,14 @@ class Container:
|
|
42
42
|
proxy_port: Optional[int] = None,
|
43
43
|
authz: Optional[V1AuthzConfig] = None,
|
44
44
|
config: Optional[GlobalConfig] = None,
|
45
|
+
api_key: Optional[str] = None,
|
45
46
|
):
|
46
47
|
# Fallback to a default config if none is provided
|
47
48
|
config = config or GlobalConfig.read()
|
48
49
|
current_server = config.get_current_server_config()
|
49
50
|
if not current_server:
|
50
51
|
raise ValueError("No current server config found")
|
51
|
-
self.api_key = current_server.api_key
|
52
|
+
self.api_key = api_key or current_server.api_key
|
52
53
|
self.nebu_host = current_server.server
|
53
54
|
self.config = config
|
54
55
|
|
@@ -250,6 +251,7 @@ class Container:
|
|
250
251
|
name: Optional[str] = None,
|
251
252
|
namespace: Optional[str] = None,
|
252
253
|
config: Optional[GlobalConfig] = None,
|
254
|
+
api_key: Optional[str] = None,
|
253
255
|
) -> List[V1Container]:
|
254
256
|
"""
|
255
257
|
Get a list of containers that match the optional name and/or namespace filters.
|
@@ -258,7 +260,7 @@ class Container:
|
|
258
260
|
current_server = config.get_current_server_config()
|
259
261
|
if not current_server:
|
260
262
|
raise ValueError("No current server config found")
|
261
|
-
api_key = current_server.api_key
|
263
|
+
api_key = api_key or current_server.api_key
|
262
264
|
nebu_host = current_server.server
|
263
265
|
|
264
266
|
containers_url = f"{nebu_host}/v1/containers"
|
@@ -292,6 +294,7 @@ class Container:
|
|
292
294
|
name: str,
|
293
295
|
namespace: Optional[str] = None,
|
294
296
|
config: Optional[GlobalConfig] = None,
|
297
|
+
api_key: Optional[str] = None,
|
295
298
|
):
|
296
299
|
"""
|
297
300
|
Get a container from the remote server.
|
@@ -307,7 +310,7 @@ class Container:
|
|
307
310
|
current_server = out.config.get_current_server_config()
|
308
311
|
if not current_server:
|
309
312
|
raise ValueError("No current server config found")
|
310
|
-
out.api_key = current_server.api_key
|
313
|
+
out.api_key = api_key or current_server.api_key
|
311
314
|
out.nebu_host = current_server.server
|
312
315
|
out.containers_url = f"{out.nebu_host}/v1/containers"
|
313
316
|
|
nebu/namespaces/namespace.py
CHANGED
@@ -22,6 +22,7 @@ class Namespace:
|
|
22
22
|
labels: Optional[Dict[str, str]] = None,
|
23
23
|
owner: Optional[str] = None,
|
24
24
|
config: Optional[GlobalConfig] = None,
|
25
|
+
api_key: Optional[str] = None,
|
25
26
|
):
|
26
27
|
self.config = config or GlobalConfig.read()
|
27
28
|
if not self.config:
|
@@ -30,7 +31,7 @@ class Namespace:
|
|
30
31
|
if not current_server:
|
31
32
|
raise ValueError("No server config found")
|
32
33
|
self.current_server = current_server
|
33
|
-
self.api_key = current_server.api_key
|
34
|
+
self.api_key = api_key or current_server.api_key
|
34
35
|
self.nebu_host = current_server.server
|
35
36
|
self.name = name
|
36
37
|
self.labels = labels
|
@@ -80,11 +81,12 @@ class Namespace:
|
|
80
81
|
cls,
|
81
82
|
name: str,
|
82
83
|
config: Optional[GlobalConfig] = None,
|
84
|
+
api_key: Optional[str] = None,
|
83
85
|
):
|
84
86
|
"""
|
85
87
|
Get a Namespace from the remote server.
|
86
88
|
"""
|
87
|
-
namespaces = cls.get(name=name, config=config)
|
89
|
+
namespaces = cls.get(name=name, config=config, api_key=api_key)
|
88
90
|
if not namespaces:
|
89
91
|
raise ValueError("Namespace not found")
|
90
92
|
namespace_v1 = namespaces[0]
|
@@ -97,7 +99,7 @@ class Namespace:
|
|
97
99
|
out.current_server = out.config.get_current_server_config()
|
98
100
|
if not out.current_server:
|
99
101
|
raise ValueError("No server config found")
|
100
|
-
out.api_key = out.current_server.api_key
|
102
|
+
out.api_key = api_key or out.current_server.api_key
|
101
103
|
out.nebu_host = out.current_server.server
|
102
104
|
out.namespaces_url = f"{out.nebu_host}/v1/namespaces"
|
103
105
|
out.name = name
|
@@ -109,6 +111,7 @@ class Namespace:
|
|
109
111
|
cls,
|
110
112
|
name: Optional[str] = None,
|
111
113
|
config: Optional[GlobalConfig] = None,
|
114
|
+
api_key: Optional[str] = None,
|
112
115
|
) -> List[V1Namespace]:
|
113
116
|
"""
|
114
117
|
Get a list of Namespaces that optionally match the name filter.
|
@@ -119,11 +122,12 @@ class Namespace:
|
|
119
122
|
current_server = config.get_current_server_config()
|
120
123
|
if not current_server:
|
121
124
|
raise ValueError("No server config found")
|
125
|
+
api_key = api_key or current_server.api_key
|
122
126
|
namespaces_url = f"{current_server.server}/v1/namespaces"
|
123
127
|
|
124
128
|
response = requests.get(
|
125
129
|
namespaces_url,
|
126
|
-
headers={"Authorization": f"Bearer {
|
130
|
+
headers={"Authorization": f"Bearer {api_key}"},
|
127
131
|
)
|
128
132
|
response.raise_for_status()
|
129
133
|
|
nebu/processors/consumer.py
CHANGED
@@ -919,69 +919,89 @@ consumer_name = f"consumer-{os.getpid()}-{socket.gethostname()}" # More unique
|
|
919
919
|
MIN_IDLE_TIME_MS = 60000 # Minimum idle time in milliseconds (e.g., 60 seconds)
|
920
920
|
CLAIM_COUNT = 10 # Max messages to claim at once
|
921
921
|
|
922
|
+
# Check if hot reloading should be disabled
|
923
|
+
disable_hot_reload = os.environ.get("NEBU_DISABLE_HOT_RELOAD", "0").lower() in [
|
924
|
+
"1",
|
925
|
+
"true",
|
926
|
+
]
|
927
|
+
print(
|
928
|
+
f"[Consumer] Hot code reloading is {'DISABLED' if disable_hot_reload else 'ENABLED'}."
|
929
|
+
)
|
930
|
+
|
922
931
|
try:
|
923
932
|
while True:
|
924
933
|
print(
|
925
934
|
f"[{datetime.now(timezone.utc).isoformat()}] --- Top of main loop ---"
|
926
935
|
) # Added log
|
927
936
|
# --- Check for Code Updates ---
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
f"[Consumer] Detected change in entrypoint file: {entrypoint_abs_path}. Reloading code..."
|
937
|
-
)
|
938
|
-
(
|
939
|
-
reloaded_target_func,
|
940
|
-
reloaded_init_func,
|
941
|
-
reloaded_module,
|
942
|
-
reloaded_namespace,
|
943
|
-
new_mtime,
|
944
|
-
) = load_or_reload_user_code(
|
945
|
-
_module_path,
|
946
|
-
_function_name,
|
947
|
-
entrypoint_abs_path,
|
948
|
-
_init_func_name,
|
949
|
-
_included_object_sources,
|
950
|
-
)
|
951
|
-
|
952
|
-
if reloaded_target_func is not None and reloaded_module is not None:
|
953
|
-
print("[Consumer] Code reload successful. Updating functions.")
|
954
|
-
target_function = reloaded_target_func
|
955
|
-
init_function = reloaded_init_func # Update init ref too, though it's already run
|
956
|
-
imported_module = reloaded_module
|
957
|
-
local_namespace = (
|
958
|
-
reloaded_namespace # Update namespace from includes
|
959
|
-
)
|
960
|
-
last_load_mtime = new_mtime
|
961
|
-
else:
|
937
|
+
if not disable_hot_reload:
|
938
|
+
print(
|
939
|
+
f"[{datetime.now(timezone.utc).isoformat()}] Checking for code updates..."
|
940
|
+
) # Added log
|
941
|
+
if entrypoint_abs_path: # Should always be set after init
|
942
|
+
try:
|
943
|
+
current_mtime = os.path.getmtime(entrypoint_abs_path)
|
944
|
+
if current_mtime > last_load_mtime:
|
962
945
|
print(
|
963
|
-
"[Consumer]
|
946
|
+
f"[Consumer] Detected change in entrypoint file: {entrypoint_abs_path}. Reloading code..."
|
947
|
+
)
|
948
|
+
(
|
949
|
+
reloaded_target_func,
|
950
|
+
reloaded_init_func,
|
951
|
+
reloaded_module,
|
952
|
+
reloaded_namespace,
|
953
|
+
new_mtime,
|
954
|
+
) = load_or_reload_user_code(
|
955
|
+
_module_path,
|
956
|
+
_function_name,
|
957
|
+
entrypoint_abs_path,
|
958
|
+
_init_func_name,
|
959
|
+
_included_object_sources,
|
964
960
|
)
|
965
|
-
# Optionally: Send an alert/log prominently that reload failed
|
966
961
|
|
967
|
-
|
962
|
+
if (
|
963
|
+
reloaded_target_func is not None
|
964
|
+
and reloaded_module is not None
|
965
|
+
):
|
966
|
+
print(
|
967
|
+
"[Consumer] Code reload successful. Updating functions."
|
968
|
+
)
|
969
|
+
target_function = reloaded_target_func
|
970
|
+
init_function = reloaded_init_func # Update init ref too, though it's already run
|
971
|
+
imported_module = reloaded_module
|
972
|
+
local_namespace = (
|
973
|
+
reloaded_namespace # Update namespace from includes
|
974
|
+
)
|
975
|
+
last_load_mtime = new_mtime
|
976
|
+
else:
|
977
|
+
print(
|
978
|
+
"[Consumer] Code reload failed. Continuing with previously loaded code."
|
979
|
+
)
|
980
|
+
# Optionally: Send an alert/log prominently that reload failed
|
981
|
+
|
982
|
+
except FileNotFoundError:
|
983
|
+
print(
|
984
|
+
f"[Consumer] Error: Entrypoint file '{entrypoint_abs_path}' not found during check. Cannot reload."
|
985
|
+
)
|
986
|
+
# Mark as non-runnable? Or just log?
|
987
|
+
target_function = None # Stop processing until file reappears?
|
988
|
+
imported_module = None
|
989
|
+
last_load_mtime = 0 # Reset mtime to force check next time
|
990
|
+
except Exception as e_reload_check:
|
991
|
+
print(f"[Consumer] Error checking/reloading code: {e_reload_check}")
|
992
|
+
traceback.print_exc()
|
993
|
+
else:
|
968
994
|
print(
|
969
|
-
|
995
|
+
"[Consumer] Warning: Entrypoint absolute path not set, cannot check for code updates."
|
970
996
|
)
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
last_load_mtime = 0 # Reset mtime to force check next time
|
975
|
-
except Exception as e_reload_check:
|
976
|
-
print(f"[Consumer] Error checking/reloading code: {e_reload_check}")
|
977
|
-
traceback.print_exc()
|
997
|
+
print(
|
998
|
+
f"[{datetime.now(timezone.utc).isoformat()}] Finished checking for code updates."
|
999
|
+
) # Added log
|
978
1000
|
else:
|
1001
|
+
# Log that hot reload is skipped if it's disabled
|
979
1002
|
print(
|
980
|
-
"[
|
1003
|
+
f"[{datetime.now(timezone.utc).isoformat()}] Hot reload check skipped (NEBU_DISABLE_HOT_RELOAD=1)."
|
981
1004
|
)
|
982
|
-
print(
|
983
|
-
f"[{datetime.now(timezone.utc).isoformat()}] Finished checking for code updates."
|
984
|
-
) # Added log
|
985
1005
|
|
986
1006
|
# --- Claim Old Pending Messages ---
|
987
1007
|
print(
|
nebu/processors/decorate.py
CHANGED
@@ -392,6 +392,7 @@ def processor(
|
|
392
392
|
health_check: Optional[V1ContainerHealthCheck] = None,
|
393
393
|
execution_mode: str = "inline",
|
394
394
|
config: Optional[GlobalConfig] = None,
|
395
|
+
hot_reload: bool = True,
|
395
396
|
):
|
396
397
|
def decorator(
|
397
398
|
func: Callable[[Any], Any],
|
@@ -1052,6 +1053,28 @@ def processor(
|
|
1052
1053
|
all_env.append(V1EnvVar(key="NEBU_EXECUTION_MODE", value=execution_mode))
|
1053
1054
|
print(f"[DEBUG Decorator] Set NEBU_EXECUTION_MODE to: {execution_mode}")
|
1054
1055
|
|
1056
|
+
# Add Hot Reload Configuration
|
1057
|
+
if not hot_reload:
|
1058
|
+
all_env.append(V1EnvVar(key="NEBU_DISABLE_HOT_RELOAD", value="1"))
|
1059
|
+
print(
|
1060
|
+
"[DEBUG Decorator] Set NEBU_DISABLE_HOT_RELOAD to: 1 (Hot reload disabled)"
|
1061
|
+
)
|
1062
|
+
else:
|
1063
|
+
# Ensure it's explicitly '0' or unset if enabled (consumer defaults to enabled if var missing)
|
1064
|
+
# Setting to "0" might be clearer than removing it if it was added by other means.
|
1065
|
+
# Check if it exists and update, otherwise add "0"
|
1066
|
+
existing_hot_reload_var = next(
|
1067
|
+
(var for var in all_env if var.key == "NEBU_DISABLE_HOT_RELOAD"), None
|
1068
|
+
)
|
1069
|
+
if existing_hot_reload_var:
|
1070
|
+
existing_hot_reload_var.value = "0"
|
1071
|
+
else:
|
1072
|
+
# Not strictly needed as consumer defaults to enabled, but explicit is good.
|
1073
|
+
all_env.append(V1EnvVar(key="NEBU_DISABLE_HOT_RELOAD", value="0"))
|
1074
|
+
print(
|
1075
|
+
"[DEBUG Decorator] Hot reload enabled (NEBU_DISABLE_HOT_RELOAD=0 or unset)"
|
1076
|
+
)
|
1077
|
+
|
1055
1078
|
# Add PYTHONPATH
|
1056
1079
|
pythonpath_value = CONTAINER_CODE_DIR
|
1057
1080
|
existing_pythonpath = next(
|
nebu/processors/processor.py
CHANGED
@@ -97,6 +97,7 @@ class Processor(Generic[InputType, OutputType]):
|
|
97
97
|
max_replicas: Optional[int] = None,
|
98
98
|
scale_config: Optional[V1Scale] = None,
|
99
99
|
config: Optional[GlobalConfig] = None,
|
100
|
+
api_key: Optional[str] = None,
|
100
101
|
no_delete: bool = False,
|
101
102
|
):
|
102
103
|
self.config = config or GlobalConfig.read()
|
@@ -106,7 +107,7 @@ class Processor(Generic[InputType, OutputType]):
|
|
106
107
|
if not current_server:
|
107
108
|
raise ValueError("No server config found")
|
108
109
|
self.current_server = current_server
|
109
|
-
self.api_key = current_server.api_key
|
110
|
+
self.api_key = api_key or current_server.api_key
|
110
111
|
self.orign_host = current_server.server
|
111
112
|
self.name = name
|
112
113
|
self.namespace = namespace
|
@@ -298,11 +299,14 @@ class Processor(Generic[InputType, OutputType]):
|
|
298
299
|
name: str,
|
299
300
|
namespace: Optional[str] = None,
|
300
301
|
config: Optional[GlobalConfig] = None,
|
302
|
+
api_key: Optional[str] = None,
|
301
303
|
):
|
302
304
|
"""
|
303
305
|
Get a Processor from the remote server.
|
304
306
|
"""
|
305
|
-
processors = cls.get(
|
307
|
+
processors = cls.get(
|
308
|
+
namespace=namespace, name=name, config=config, api_key=api_key
|
309
|
+
)
|
306
310
|
if not processors:
|
307
311
|
raise ValueError("Processor not found")
|
308
312
|
processor_v1 = processors[0]
|
@@ -315,7 +319,7 @@ class Processor(Generic[InputType, OutputType]):
|
|
315
319
|
out.current_server = out.config.get_current_server_config()
|
316
320
|
if not out.current_server:
|
317
321
|
raise ValueError("No server config found")
|
318
|
-
out.api_key = out.current_server.api_key
|
322
|
+
out.api_key = api_key or out.current_server.api_key
|
319
323
|
out.orign_host = out.current_server.server
|
320
324
|
out.processors_url = f"{out.orign_host}/v1/processors"
|
321
325
|
out.name = name
|
@@ -337,6 +341,7 @@ class Processor(Generic[InputType, OutputType]):
|
|
337
341
|
name: Optional[str] = None,
|
338
342
|
namespace: Optional[str] = None,
|
339
343
|
config: Optional[GlobalConfig] = None,
|
344
|
+
api_key: Optional[str] = None,
|
340
345
|
) -> List[V1Processor]:
|
341
346
|
"""
|
342
347
|
Get a list of Processors that match the optional name and/or namespace filters.
|
@@ -351,7 +356,7 @@ class Processor(Generic[InputType, OutputType]):
|
|
351
356
|
|
352
357
|
response = requests.get(
|
353
358
|
processors_url,
|
354
|
-
headers={"Authorization": f"Bearer {current_server.api_key}"},
|
359
|
+
headers={"Authorization": f"Bearer {api_key or current_server.api_key}"},
|
355
360
|
)
|
356
361
|
response.raise_for_status()
|
357
362
|
|
@@ -8,23 +8,23 @@ nebu/meta.py,sha256=CzFHMND9seuewzq9zNNx9WTr6JvrCBExe7BLqDSr7lM,745
|
|
8
8
|
nebu/orign.py,sha256=SkVfHgpadwik58KCZCrjdV5EHY0dhpEhDvijzLxY11Y,2052
|
9
9
|
nebu/builders/builder.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
nebu/builders/models.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
nebu/containers/container.py,sha256=
|
11
|
+
nebu/containers/container.py,sha256=jyqG-WLFMAFzYLW9Bagi74zJ-zUPq_jHmrxF_2HVWjQ,13652
|
12
12
|
nebu/containers/decorator.py,sha256=uFtzlAXRHYZECJ-NPusY7oN9GXvdHrHDd_JNrIGr8aQ,3244
|
13
13
|
nebu/containers/models.py,sha256=0j6NGy4yto-enRDh_4JH_ZTbHrLdSpuMOqNQPnIrwC4,6815
|
14
14
|
nebu/containers/server.py,sha256=yFa2Y9PzBn59E1HftKiv0iapPonli2rbGAiU6r-wwe0,2513
|
15
15
|
nebu/namespaces/models.py,sha256=EqUOpzhVBhvJw2P92ONDUbIgC31M9jMmcaG5vyOrsWg,497
|
16
|
-
nebu/namespaces/namespace.py,sha256=
|
17
|
-
nebu/processors/consumer.py,sha256=
|
16
|
+
nebu/namespaces/namespace.py,sha256=LsbGiGBzVtFUtxCRayGrqr2X1tDzRep4RnNklfWCC1k,5160
|
17
|
+
nebu/processors/consumer.py,sha256=RNc4DCuAcmFfSDIyprJrACuVfog6V5cWoqWt3zDRGqQ,53141
|
18
18
|
nebu/processors/consumer_process_worker.py,sha256=UAmhrR1wilQnRPbbHYZ9jaIrDKs0LKsSHxbj4VFvFcQ,31969
|
19
|
-
nebu/processors/decorate.py,sha256=
|
19
|
+
nebu/processors/decorate.py,sha256=w8ZJwe8MNhBoW_LJzUWHOgXvo-39RnPsGvRUXSd9Hk4,55784
|
20
20
|
nebu/processors/default.py,sha256=W4slJenG59rvyTlJ7gRp58eFfXcNOTT2Hfi6zzJAobI,365
|
21
21
|
nebu/processors/models.py,sha256=FnBJFxtaJkp-uIOs90qkJUBvOR80l2cdGnfmOIWIvVA,4058
|
22
|
-
nebu/processors/processor.py,sha256=
|
22
|
+
nebu/processors/processor.py,sha256=h1ZmD1rqTSuC9QpHkobx6VCUZoVkKXoDBLaB_xxfjys,16037
|
23
23
|
nebu/processors/remote.py,sha256=TeAIPGEMqnDIb7H1iett26IEZrBlcbPB_-DSm6jcH1E,1285
|
24
24
|
nebu/redis/models.py,sha256=coPovAcVXnOU1Xh_fpJL4PO3QctgK9nBe5QYoqEcnxg,1230
|
25
25
|
nebu/services/service.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
|
-
nebu-0.1.
|
27
|
-
nebu-0.1.
|
28
|
-
nebu-0.1.
|
29
|
-
nebu-0.1.
|
30
|
-
nebu-0.1.
|
26
|
+
nebu-0.1.91.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
27
|
+
nebu-0.1.91.dist-info/METADATA,sha256=LVAMxSj83DGy_CAHMuY1RxH3UYAn2wQ5JSoecIxQeY0,1731
|
28
|
+
nebu-0.1.91.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
|
29
|
+
nebu-0.1.91.dist-info/top_level.txt,sha256=uLIbEKJeGSHWOAJN5S0i5XBGwybALlF9bYoB1UhdEgQ,5
|
30
|
+
nebu-0.1.91.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|