moose-lib 0.6.157__tar.gz → 0.6.158__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.

Potentially problematic release.


This version of moose-lib might be problematic. Click here for more details.

Files changed (54) hide show
  1. {moose_lib-0.6.157 → moose_lib-0.6.158}/PKG-INFO +1 -1
  2. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/__init__.py +2 -0
  3. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/olap_table.py +1 -0
  4. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/internal.py +3 -1
  5. moose_lib-0.6.158/moose_lib/secrets.py +100 -0
  6. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib.egg-info/PKG-INFO +1 -1
  7. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib.egg-info/SOURCES.txt +2 -0
  8. moose_lib-0.6.158/tests/test_secrets.py +221 -0
  9. {moose_lib-0.6.157 → moose_lib-0.6.158}/README.md +0 -0
  10. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/blocks.py +0 -0
  11. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/clients/__init__.py +0 -0
  12. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/clients/redis_client.py +0 -0
  13. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/commons.py +0 -0
  14. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/config/__init__.py +0 -0
  15. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/config/config_file.py +0 -0
  16. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/config/runtime.py +0 -0
  17. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/data_models.py +0 -0
  18. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/__init__.py +0 -0
  19. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/_registry.py +0 -0
  20. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/consumption.py +0 -0
  21. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/ingest_api.py +0 -0
  22. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/ingest_pipeline.py +0 -0
  23. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/life_cycle.py +0 -0
  24. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/materialized_view.py +0 -0
  25. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/registry.py +0 -0
  26. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/sql_resource.py +0 -0
  27. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/stream.py +0 -0
  28. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/types.py +0 -0
  29. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/view.py +0 -0
  30. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/web_app.py +0 -0
  31. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/web_app_helpers.py +0 -0
  32. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2/workflow.py +0 -0
  33. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/dmv2_serializer.py +0 -0
  34. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/main.py +0 -0
  35. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/query_builder.py +0 -0
  36. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/query_param.py +0 -0
  37. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/streaming/__init__.py +0 -0
  38. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/streaming/streaming_function_runner.py +0 -0
  39. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/utilities/__init__.py +0 -0
  40. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib/utilities/sql.py +0 -0
  41. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib.egg-info/dependency_links.txt +0 -0
  42. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib.egg-info/requires.txt +0 -0
  43. {moose_lib-0.6.157 → moose_lib-0.6.158}/moose_lib.egg-info/top_level.txt +0 -0
  44. {moose_lib-0.6.157 → moose_lib-0.6.158}/setup.cfg +0 -0
  45. {moose_lib-0.6.157 → moose_lib-0.6.158}/setup.py +0 -0
  46. {moose_lib-0.6.157 → moose_lib-0.6.158}/tests/__init__.py +0 -0
  47. {moose_lib-0.6.157 → moose_lib-0.6.158}/tests/conftest.py +0 -0
  48. {moose_lib-0.6.157 → moose_lib-0.6.158}/tests/test_moose.py +0 -0
  49. {moose_lib-0.6.157 → moose_lib-0.6.158}/tests/test_olap_table_versioning.py +0 -0
  50. {moose_lib-0.6.157 → moose_lib-0.6.158}/tests/test_query_builder.py +0 -0
  51. {moose_lib-0.6.157 → moose_lib-0.6.158}/tests/test_redis_client.py +0 -0
  52. {moose_lib-0.6.157 → moose_lib-0.6.158}/tests/test_s3queue_config.py +0 -0
  53. {moose_lib-0.6.157 → moose_lib-0.6.158}/tests/test_simple_aggregate.py +0 -0
  54. {moose_lib-0.6.157 → moose_lib-0.6.158}/tests/test_web_app.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moose_lib
3
- Version: 0.6.157
3
+ Version: 0.6.158
4
4
  Home-page: https://www.fiveonefour.com/moose
5
5
  Author: Fiveonefour Labs Inc.
6
6
  Author-email: support@fiveonefour.com
@@ -4,6 +4,8 @@ from .blocks import *
4
4
 
5
5
  from .commons import *
6
6
 
7
+ from .secrets import moose_runtime_env
8
+
7
9
  from .data_models import *
8
10
 
9
11
  from .dmv2 import *
@@ -143,6 +143,7 @@ class OlapConfig(BaseModel):
143
143
  granularity: int = 1
144
144
 
145
145
  indexes: list[TableIndex] = []
146
+ database: Optional[str] = None # Optional database name for multi-database support
146
147
 
147
148
  def model_post_init(self, __context):
148
149
  has_fields = bool(self.order_by_fields)
@@ -171,6 +171,7 @@ class TableConfig(BaseModel):
171
171
  table_settings: Optional[dict[str, str]] = None
172
172
  indexes: list[OlapConfig.TableIndex] = []
173
173
  ttl: Optional[str] = None
174
+ database: Optional[str] = None
174
175
 
175
176
 
176
177
  class TopicConfig(BaseModel):
@@ -613,6 +614,7 @@ def to_infra_map() -> dict:
613
614
  table_settings=table_settings if table_settings else None,
614
615
  indexes=table.config.indexes,
615
616
  ttl=table.config.ttl,
617
+ database=table.config.database,
616
618
  )
617
619
 
618
620
  for name, stream in get_streams().items():
@@ -716,7 +718,7 @@ def to_infra_map() -> dict:
716
718
  web_apps=web_apps
717
719
  )
718
720
 
719
- return infra_map.model_dump(by_alias=True)
721
+ return infra_map.model_dump(by_alias=True, exclude_none=False)
720
722
 
721
723
 
722
724
  def load_models():
@@ -0,0 +1,100 @@
1
+ """Utilities for runtime environment variable resolution.
2
+
3
+ This module provides functionality to mark values that should be resolved
4
+ from environment variables at runtime by the Moose CLI, rather than being
5
+ embedded at build time.
6
+
7
+ Example:
8
+ >>> from moose_lib import S3QueueEngine, moose_runtime_env
9
+ >>>
10
+ >>> engine = S3QueueEngine(
11
+ ... s3_path="s3://bucket/data/*.json",
12
+ ... format="JSONEachRow",
13
+ ... aws_access_key_id=moose_runtime_env.get("AWS_ACCESS_KEY_ID"),
14
+ ... aws_secret_access_key=moose_runtime_env.get("AWS_SECRET_ACCESS_KEY")
15
+ ... )
16
+ """
17
+
18
+ #: Prefix used to mark values for runtime environment variable resolution.
19
+ MOOSE_RUNTIME_ENV_PREFIX = "__MOOSE_RUNTIME_ENV__:"
20
+
21
+
22
+ def get(env_var_name: str) -> str:
23
+ """Marks a value to be resolved from an environment variable at runtime.
24
+
25
+ When you use this function, the value is not read immediately. Instead,
26
+ a special marker is created that the Moose CLI will resolve when it
27
+ processes your infrastructure configuration.
28
+
29
+ This is useful for:
30
+ - Credentials that should never be embedded in Docker images
31
+ - Configuration that can be rotated without rebuilding
32
+ - Different values for different environments (dev, staging, prod)
33
+ - Any runtime configuration in infrastructure elements (Tables, Topics, etc.)
34
+
35
+ Args:
36
+ env_var_name: Name of the environment variable to resolve
37
+
38
+ Returns:
39
+ A marker string that Moose CLI will resolve at runtime
40
+
41
+ Raises:
42
+ ValueError: If the environment variable name is empty
43
+
44
+ Example:
45
+ >>> # Instead of this (evaluated at build time):
46
+ >>> import os
47
+ >>> aws_key = os.environ.get("AWS_ACCESS_KEY_ID")
48
+ >>>
49
+ >>> # Use this (evaluated at runtime):
50
+ >>> aws_key = moose_runtime_env.get("AWS_ACCESS_KEY_ID")
51
+ """
52
+ if not env_var_name or not env_var_name.strip():
53
+ raise ValueError("Environment variable name cannot be empty")
54
+ return f"{MOOSE_RUNTIME_ENV_PREFIX}{env_var_name}"
55
+
56
+
57
+ class MooseRuntimeEnv:
58
+ """Utilities for marking values to be resolved from environment variables at runtime.
59
+
60
+ This class provides a namespace for runtime environment variable resolution.
61
+ Use the singleton instance `moose_runtime_env` rather than instantiating this class directly.
62
+
63
+ Attributes:
64
+ get: Static method for creating runtime environment variable markers
65
+ """
66
+
67
+ @staticmethod
68
+ def get(env_var_name: str) -> str:
69
+ """Marks a value to be resolved from an environment variable at runtime.
70
+
71
+ Args:
72
+ env_var_name: Name of the environment variable to resolve
73
+
74
+ Returns:
75
+ A marker string that Moose CLI will resolve at runtime
76
+
77
+ Raises:
78
+ ValueError: If the environment variable name is empty
79
+ """
80
+ return get(env_var_name)
81
+
82
+
83
+ # Export singleton instance for module-level access
84
+ moose_runtime_env = MooseRuntimeEnv()
85
+
86
+ # Legacy exports for backwards compatibility
87
+ MooseEnvSecrets = MooseRuntimeEnv # Deprecated: Use MooseRuntimeEnv instead
88
+ moose_env_secrets = moose_runtime_env # Deprecated: Use moose_runtime_env instead
89
+ MOOSE_ENV_SECRET_PREFIX = MOOSE_RUNTIME_ENV_PREFIX # Deprecated: Use MOOSE_RUNTIME_ENV_PREFIX instead
90
+
91
+ __all__ = [
92
+ "moose_runtime_env",
93
+ "MooseRuntimeEnv",
94
+ "get",
95
+ "MOOSE_RUNTIME_ENV_PREFIX",
96
+ # Legacy exports (deprecated)
97
+ "moose_env_secrets",
98
+ "MooseEnvSecrets",
99
+ "MOOSE_ENV_SECRET_PREFIX",
100
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: moose_lib
3
- Version: 0.6.157
3
+ Version: 0.6.158
4
4
  Home-page: https://www.fiveonefour.com/moose
5
5
  Author: Fiveonefour Labs Inc.
6
6
  Author-email: support@fiveonefour.com
@@ -9,6 +9,7 @@ moose_lib/internal.py
9
9
  moose_lib/main.py
10
10
  moose_lib/query_builder.py
11
11
  moose_lib/query_param.py
12
+ moose_lib/secrets.py
12
13
  moose_lib.egg-info/PKG-INFO
13
14
  moose_lib.egg-info/SOURCES.txt
14
15
  moose_lib.egg-info/dependency_links.txt
@@ -46,5 +47,6 @@ tests/test_olap_table_versioning.py
46
47
  tests/test_query_builder.py
47
48
  tests/test_redis_client.py
48
49
  tests/test_s3queue_config.py
50
+ tests/test_secrets.py
49
51
  tests/test_simple_aggregate.py
50
52
  tests/test_web_app.py
@@ -0,0 +1,221 @@
1
+ """Tests for the moose_lib.secrets module.
2
+
3
+ This module tests the runtime environment variable marker functionality,
4
+ which allows users to defer secret resolution until runtime rather than
5
+ embedding secrets at build time.
6
+ """
7
+
8
+ import pytest
9
+ from moose_lib.secrets import moose_runtime_env, get, MOOSE_RUNTIME_ENV_PREFIX
10
+
11
+
12
+ class TestMooseRuntimeEnvGet:
13
+ """Tests for the moose_runtime_env.get() method."""
14
+
15
+ def test_creates_marker_with_correct_prefix(self):
16
+ """Should create a marker string with the correct prefix."""
17
+ var_name = "AWS_ACCESS_KEY_ID"
18
+ result = moose_runtime_env.get(var_name)
19
+
20
+ assert result == f"{MOOSE_RUNTIME_ENV_PREFIX}{var_name}"
21
+ assert result == "__MOOSE_RUNTIME_ENV__:AWS_ACCESS_KEY_ID"
22
+
23
+ def test_handles_different_variable_names(self):
24
+ """Should handle different environment variable names correctly."""
25
+ test_cases = [
26
+ "AWS_SECRET_ACCESS_KEY",
27
+ "DATABASE_PASSWORD",
28
+ "API_KEY",
29
+ "MY_CUSTOM_SECRET",
30
+ ]
31
+
32
+ for var_name in test_cases:
33
+ result = moose_runtime_env.get(var_name)
34
+ assert result == f"{MOOSE_RUNTIME_ENV_PREFIX}{var_name}"
35
+ assert var_name in result
36
+
37
+ def test_raises_error_for_empty_string(self):
38
+ """Should raise ValueError for empty string."""
39
+ with pytest.raises(ValueError, match="Environment variable name cannot be empty"):
40
+ moose_runtime_env.get("")
41
+
42
+ def test_raises_error_for_whitespace_only(self):
43
+ """Should raise ValueError for whitespace-only string."""
44
+ with pytest.raises(ValueError, match="Environment variable name cannot be empty"):
45
+ moose_runtime_env.get(" ")
46
+
47
+ def test_raises_error_for_tabs_only(self):
48
+ """Should raise ValueError for string with only tabs."""
49
+ with pytest.raises(ValueError, match="Environment variable name cannot be empty"):
50
+ moose_runtime_env.get("\t\t")
51
+
52
+ def test_allows_underscores_in_variable_names(self):
53
+ """Should allow variable names with underscores."""
54
+ var_name = "MY_LONG_VAR_NAME"
55
+ result = moose_runtime_env.get(var_name)
56
+
57
+ assert result == f"{MOOSE_RUNTIME_ENV_PREFIX}{var_name}"
58
+
59
+ def test_allows_numbers_in_variable_names(self):
60
+ """Should allow variable names with numbers."""
61
+ var_name = "API_KEY_123"
62
+ result = moose_runtime_env.get(var_name)
63
+
64
+ assert result == f"{MOOSE_RUNTIME_ENV_PREFIX}{var_name}"
65
+
66
+ def test_preserves_exact_casing(self):
67
+ """Should preserve exact variable name casing."""
68
+ var_name = "MixedCase_VarName"
69
+ result = moose_runtime_env.get(var_name)
70
+
71
+ assert var_name in result
72
+ assert var_name.lower() not in result # Ensure casing wasn't changed
73
+
74
+ def test_can_be_used_in_s3queue_config(self):
75
+ """Should create markers that can be used in S3Queue configuration."""
76
+ access_key_marker = moose_runtime_env.get("AWS_ACCESS_KEY_ID")
77
+ secret_key_marker = moose_runtime_env.get("AWS_SECRET_ACCESS_KEY")
78
+
79
+ config = {
80
+ "aws_access_key_id": access_key_marker,
81
+ "aws_secret_access_key": secret_key_marker,
82
+ }
83
+
84
+ assert "AWS_ACCESS_KEY_ID" in config["aws_access_key_id"]
85
+ assert "AWS_SECRET_ACCESS_KEY" in config["aws_secret_access_key"]
86
+
87
+
88
+ class TestModuleLevelGetFunction:
89
+ """Tests for the module-level get() function."""
90
+
91
+ def test_module_level_get_creates_marker(self):
92
+ """The module-level get function should create markers."""
93
+ var_name = "TEST_SECRET"
94
+ result = get(var_name)
95
+
96
+ assert result == f"{MOOSE_RUNTIME_ENV_PREFIX}{var_name}"
97
+
98
+ def test_module_level_get_matches_class_method(self):
99
+ """Module-level get should produce same result as class method."""
100
+ var_name = "MY_SECRET"
101
+
102
+ result_module = get(var_name)
103
+ result_class = moose_runtime_env.get(var_name)
104
+
105
+ assert result_module == result_class
106
+
107
+ def test_module_level_get_raises_error_for_empty(self):
108
+ """Module-level get should raise ValueError for empty string."""
109
+ with pytest.raises(ValueError, match="Environment variable name cannot be empty"):
110
+ get("")
111
+
112
+
113
+ class TestMooseRuntimeEnvPrefix:
114
+ """Tests for the MOOSE_RUNTIME_ENV_PREFIX constant."""
115
+
116
+ def test_has_expected_value(self):
117
+ """Should have the expected prefix value."""
118
+ assert MOOSE_RUNTIME_ENV_PREFIX == "__MOOSE_RUNTIME_ENV__:"
119
+
120
+ def test_is_string(self):
121
+ """Should be a string."""
122
+ assert isinstance(MOOSE_RUNTIME_ENV_PREFIX, str)
123
+
124
+ def test_is_not_empty(self):
125
+ """Should not be empty."""
126
+ assert len(MOOSE_RUNTIME_ENV_PREFIX) > 0
127
+
128
+
129
+ class TestMarkerFormatValidation:
130
+ """Tests for marker format validation and parsing."""
131
+
132
+ def test_creates_easily_detectable_markers(self):
133
+ """Should create markers that are easily detectable."""
134
+ marker = moose_runtime_env.get("TEST_VAR")
135
+
136
+ assert marker.startswith("__MOOSE_RUNTIME_ENV__:")
137
+
138
+ def test_markers_can_be_split_to_extract_variable_name(self):
139
+ """Should create markers that can be split to extract variable name."""
140
+ var_name = "MY_SECRET"
141
+ marker = moose_runtime_env.get(var_name)
142
+
143
+ parts = marker.split(MOOSE_RUNTIME_ENV_PREFIX)
144
+ assert len(parts) == 2
145
+ assert parts[1] == var_name
146
+
147
+ def test_markers_are_json_serializable(self):
148
+ """Should create markers that are JSON serializable."""
149
+ import json
150
+
151
+ marker = moose_runtime_env.get("TEST_VAR")
152
+ json_str = json.dumps({"secret": marker})
153
+ parsed = json.loads(json_str)
154
+
155
+ assert parsed["secret"] == marker
156
+
157
+ def test_markers_work_with_dict_serialization(self):
158
+ """Should work correctly with dictionary serialization."""
159
+ marker = moose_runtime_env.get("DATABASE_PASSWORD")
160
+
161
+ config = {
162
+ "password": marker,
163
+ "other_field": "value"
164
+ }
165
+
166
+ # Verify the marker is preserved in the dict
167
+ assert config["password"] == marker
168
+ assert MOOSE_RUNTIME_ENV_PREFIX in config["password"]
169
+
170
+
171
+ class TestIntegrationScenarios:
172
+ """Integration tests for real-world usage scenarios."""
173
+
174
+ def test_s3queue_engine_with_secrets(self):
175
+ """Should work correctly in S3Queue engine configuration."""
176
+ from moose_lib.blocks import S3QueueEngine
177
+
178
+ engine = S3QueueEngine(
179
+ s3_path="s3://my-bucket/data/*.json",
180
+ format="JSONEachRow",
181
+ aws_access_key_id=moose_runtime_env.get("AWS_ACCESS_KEY_ID"),
182
+ aws_secret_access_key=moose_runtime_env.get("AWS_SECRET_ACCESS_KEY"),
183
+ )
184
+
185
+ # Verify markers were set correctly
186
+ assert engine.aws_access_key_id == "__MOOSE_RUNTIME_ENV__:AWS_ACCESS_KEY_ID"
187
+ assert engine.aws_secret_access_key == "__MOOSE_RUNTIME_ENV__:AWS_SECRET_ACCESS_KEY"
188
+
189
+ def test_multiple_secrets_in_same_config(self):
190
+ """Should handle multiple secrets in the same configuration."""
191
+ config = {
192
+ "username": moose_runtime_env.get("DB_USERNAME"),
193
+ "password": moose_runtime_env.get("DB_PASSWORD"),
194
+ "api_key": moose_runtime_env.get("API_KEY"),
195
+ }
196
+
197
+ # All should have the correct prefix
198
+ for value in config.values():
199
+ assert value.startswith(MOOSE_RUNTIME_ENV_PREFIX)
200
+
201
+ # Each should have the correct variable name
202
+ assert "DB_USERNAME" in config["username"]
203
+ assert "DB_PASSWORD" in config["password"]
204
+ assert "API_KEY" in config["api_key"]
205
+
206
+ def test_mixed_secret_and_plain_values(self):
207
+ """Should handle mix of secret markers and plain values."""
208
+ config = {
209
+ "region": "us-east-1", # Plain value
210
+ "access_key": moose_runtime_env.get("AWS_ACCESS_KEY_ID"), # Secret
211
+ "bucket": "my-bucket", # Plain value
212
+ "secret_key": moose_runtime_env.get("AWS_SECRET_ACCESS_KEY"), # Secret
213
+ }
214
+
215
+ # Plain values should be unchanged
216
+ assert config["region"] == "us-east-1"
217
+ assert config["bucket"] == "my-bucket"
218
+
219
+ # Secrets should have markers
220
+ assert MOOSE_RUNTIME_ENV_PREFIX in config["access_key"]
221
+ assert MOOSE_RUNTIME_ENV_PREFIX in config["secret_key"]
File without changes
File without changes
File without changes