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.
@@ -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
 
@@ -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 {current_server.api_key}"},
130
+ headers={"Authorization": f"Bearer {api_key}"},
127
131
  )
128
132
  response.raise_for_status()
129
133
 
@@ -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
- print(
929
- f"[{datetime.now(timezone.utc).isoformat()}] Checking for code updates..."
930
- ) # Added log
931
- if entrypoint_abs_path: # Should always be set after init
932
- try:
933
- current_mtime = os.path.getmtime(entrypoint_abs_path)
934
- if current_mtime > last_load_mtime:
935
- print(
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] Code reload failed. Continuing with previously loaded code."
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
- except FileNotFoundError:
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
- f"[Consumer] Error: Entrypoint file '{entrypoint_abs_path}' not found during check. Cannot reload."
995
+ "[Consumer] Warning: Entrypoint absolute path not set, cannot check for code updates."
970
996
  )
971
- # Mark as non-runnable? Or just log?
972
- target_function = None # Stop processing until file reappears?
973
- imported_module = None
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
- "[Consumer] Warning: Entrypoint absolute path not set, cannot check for code updates."
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(
@@ -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(
@@ -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(namespace=namespace, name=name, config=config)
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nebu
3
- Version: 0.1.88
3
+ Version: 0.1.91
4
4
  Summary: A globally distributed container runtime
5
5
  Requires-Python: >=3.10.14
6
6
  Description-Content-Type: text/markdown
@@ -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=yb7KaPTVXnEEAlrpdlUi4HNqF6P7z9bmwAILGlq6iqU,13502
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=Q_EDH7BgQrTkaDh_l4tbo22qpq-uARfIk8ZPBLjITGY,4967
17
- nebu/processors/consumer.py,sha256=DBlQJjbRrRiSgF5MawyO5vPlu4PEkA7c8lgZ_RQ9rJw,52266
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=jMh7OMamPdxGn7cMxQsOl5CEEmhZ1TXkMz8nCzBpVaU,54649
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=OgEK8Fz0ehSe_VFiNsxweVKZIckhgVvQQ11NNffYZqA,15848
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.88.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
27
- nebu-0.1.88.dist-info/METADATA,sha256=aD9p65LkMiOaY0qlJV4wdCjrKaPHb1rwPdQcGCMJntk,1731
28
- nebu-0.1.88.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
29
- nebu-0.1.88.dist-info/top_level.txt,sha256=uLIbEKJeGSHWOAJN5S0i5XBGwybALlF9bYoB1UhdEgQ,5
30
- nebu-0.1.88.dist-info/RECORD,,
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