params-proto 3.0.0rc24__py3-none-any.whl → 3.0.0rc26__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.
@@ -62,14 +62,16 @@ class ParameterIterator:
62
62
 
63
63
  def __mul__(self, other):
64
64
  """
65
- Cartesian product with another ParameterIterator.
65
+ Cartesian product with another ParameterIterator or dict.
66
66
 
67
67
  Example:
68
- piter1 = piter({Config.lr: [0.001, 0.01]})
69
- piter2 = piter({Config.batch_size: [32, 64]})
70
- combined = piter1 * piter2 # 4 combinations
68
+ piter @ {"lr": [0.001, 0.01]} * {"batch_size": [32, 64]}
69
+ # Creates 4 combinations
71
70
  """
72
- if not isinstance(other, ParameterIterator):
71
+ # Auto-wrap dicts as ParameterIterator
72
+ if isinstance(other, dict):
73
+ other = piter._create_iterator(other)
74
+ elif not isinstance(other, ParameterIterator):
73
75
  raise TypeError(f"Cannot multiply ParameterIterator with {type(other)}")
74
76
 
75
77
  # Materialize other to a list so we can iterate multiple times
@@ -142,57 +144,81 @@ class ParameterIterator:
142
144
  return len(self.list)
143
145
 
144
146
 
145
- def piter(spec):
147
+ class PiterFactory:
146
148
  """
147
- Create a parameter iterator from a specification dict.
149
+ Factory for creating parameter iterators.
148
150
 
149
- Args:
150
- spec: Dict mapping parameter names (strings) to values or lists of values.
151
- Keys should be parameter names, optionally with prefix (e.g., "config.lr").
152
- Values can be single values or lists/iterables.
151
+ Supports both function-call syntax and @ operator syntax:
152
+ piter({"lr": [0.001, 0.01]}) # function call
153
+ piter @ {"lr": [0.001, 0.01]} # @ operator
153
154
 
154
- Returns:
155
- ParameterIterator that zips parameter lists element-wise.
155
+ The @ operator enables clean chaining:
156
+ piter @ {"lr": [0.001, 0.01]} * piter @ {"batch_size": [32, 64]}
157
+ """
156
158
 
157
- Example:
158
- # Element-wise zip (default behavior)
159
- piter({"lr": [0.001, 0.01], "batch_size": [32, 64]})
160
- # Creates 2 configs: (0.001, 32), (0.01, 64)
159
+ def __call__(self, spec):
160
+ """Create parameter iterator (function-call syntax)."""
161
+ return self._create_iterator(spec)
161
162
 
162
- # Fixed value
163
- piter({"seed": 200})
164
- # Creates 1 config with seed=200
163
+ def __matmul__(self, spec):
164
+ """Create parameter iterator (@ operator syntax)."""
165
+ return self._create_iterator(spec)
165
166
 
166
- # For Cartesian product, use * operator:
167
- piter({"lr": [0.001, 0.01]}) * piter({"batch_size": [32, 64]})
168
- # Creates 4 configs (2 × 2)
167
+ def _create_iterator(self, spec):
168
+ """
169
+ Create a parameter iterator from a specification dict.
169
170
 
170
- # With prefixes for multiple proto classes
171
- piter({"model.depth": [18, 50], "training.lr": [0.001, 0.01]})
172
- # Creates 2 configs (zipped)
173
- """
174
- # Convert values to lists if needed
175
- params = {}
176
- for key, values in spec.items():
177
- if not isinstance(key, str):
178
- raise TypeError(f"Parameter keys must be strings, got {type(key)}")
179
- param_values = values if isinstance(values, (list, tuple, range)) else [values]
180
- params[key] = param_values
181
-
182
- # Create iterator by zipping parameter lists
183
- def zip_iter():
184
- if not params:
185
- return
171
+ Args:
172
+ spec: Dict mapping parameter names (strings) to values or lists of values.
173
+ Keys should be parameter names, optionally with prefix (e.g., "config.lr").
174
+ Values can be single values or lists/iterables.
175
+
176
+ Returns:
177
+ ParameterIterator that zips parameter lists element-wise.
178
+
179
+ Example:
180
+ # Element-wise zip (default behavior)
181
+ piter({"lr": [0.001, 0.01], "batch_size": [32, 64]})
182
+ # Creates 2 configs: (0.001, 32), (0.01, 64)
183
+
184
+ # Fixed value
185
+ piter({"seed": 200})
186
+ # Creates 1 config with seed=200
187
+
188
+ # For Cartesian product, use * operator:
189
+ piter @ {"lr": [0.001, 0.01]} * piter @ {"batch_size": [32, 64]}
190
+ # Creates 4 configs (2 × 2)
191
+
192
+ # With prefixes for multiple proto classes
193
+ piter({"model.depth": [18, 50], "training.lr": [0.001, 0.01]})
194
+ # Creates 2 configs (zipped)
195
+ """
196
+ # Convert values to lists if needed
197
+ params = {}
198
+ for key, values in spec.items():
199
+ if not isinstance(key, str):
200
+ raise TypeError(f"Parameter keys must be strings, got {type(key)}")
201
+ param_values = values if isinstance(values, (list, tuple, range)) else [values]
202
+ params[key] = param_values
203
+
204
+ # Create iterator by zipping parameter lists
205
+ def zip_iter():
206
+ if not params:
207
+ return
208
+
209
+ keys = list(params.keys())
210
+ value_lists = [params[k] for k in keys]
211
+
212
+ # Zip parameter lists element-wise
213
+ for combination in zip(*value_lists):
214
+ config = dict(zip(keys, combination))
215
+ yield config
186
216
 
187
- keys = list(params.keys())
188
- value_lists = [params[k] for k in keys]
217
+ return ParameterIterator(zip_iter())
189
218
 
190
- # Zip parameter lists element-wise
191
- for combination in zip(*value_lists):
192
- config = dict(zip(keys, combination))
193
- yield config
194
219
 
195
- return ParameterIterator(zip_iter())
220
+ # Singleton instance for use as both function and @ operator
221
+ piter = PiterFactory()
196
222
 
197
223
 
198
224
  def dot_join(*keys):
params_proto/proto.py CHANGED
@@ -755,6 +755,30 @@ def proto(
755
755
  klass_annotations = getattr(klass, "__annotations__", {})
756
756
  annotations.update(klass_annotations)
757
757
 
758
+ # Infer types for untyped class attributes from their default values
759
+ # This allows `untyped_attr = "hello"` to be treated as `untyped_attr: str = "hello"`
760
+ # For None defaults, use Any since NoneType isn't useful as a type hint
761
+ from typing import Any
762
+
763
+ for klass in reversed(obj.__mro__):
764
+ if klass is object:
765
+ continue
766
+ for name, value in vars(klass).items():
767
+ # Skip if already annotated, private/dunder, or is a method/descriptor
768
+ if name in annotations:
769
+ continue
770
+ if name.startswith("_"):
771
+ continue
772
+ if callable(value) and not (hasattr(value, "__class__") and value.__class__.__name__ == "_EnvVar"):
773
+ continue
774
+ if isinstance(value, (classmethod, staticmethod, property)):
775
+ continue
776
+ # Infer type from the default value, use Any for None
777
+ if value is None:
778
+ annotations[name] = Any
779
+ else:
780
+ annotations[name] = type(value)
781
+
758
782
  for name in annotations.keys():
759
783
  if hasattr(obj, name):
760
784
  value = getattr(obj, name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: params-proto
3
- Version: 3.0.0rc24
3
+ Version: 3.0.0rc26
4
4
  Summary: Modern Hyper Parameter Management for Machine Learning
5
5
  Project-URL: Homepage, https://github.com/geyang/params-proto
6
6
  Project-URL: Documentation, https://params-proto.readthedocs.io
@@ -119,7 +119,7 @@ uv run python scratch/demo_v3.py 42
119
119
  ## Installation
120
120
 
121
121
  ```bash
122
- pip install params-proto==3.0.0-rc7
122
+ pip install params-proto==3.0.0-rc25
123
123
  ```
124
124
 
125
125
  ## Key Features
@@ -3,7 +3,7 @@ params_proto/app.py,sha256=UySpd1op3M44Szk6Ekyn0fJcnZsQvMTMPdaEybwWsLE,19
3
3
  params_proto/documentation.py,sha256=mIqmcwGWo8tM1BuNzLIwVTzdbQ3qyPus7yWTaOce4dM,8091
4
4
  params_proto/envvar.py,sha256=Gui4AJGPiBSDSyIC3KjmXGmewFmgihAF0QEOm-ufFVo,4323
5
5
  params_proto/parse_env_template.py,sha256=mXTvKpNhT2jGr3HpwKw42shd18O0QACmSJn6yWMDdKA,1298
6
- params_proto/proto.py,sha256=oMTHLNtAMgU7qWLNOSjssGEUP0il4mt4rWdmUwfzEBg,40898
6
+ params_proto/proto.py,sha256=Q3NSmjxKZC7yKGref5F3o7KJEnALaaHWjwd1kDJRDXQ,41914
7
7
  params_proto/type_utils.py,sha256=x68rL5m76ZFRKsCRgH_i_4vLpt6ldWEsEAalgacFIH8,7364
8
8
  params_proto/cli/__init__.py,sha256=sLpN3GmaBqd_d0J0nvUNOeGlV74_-jQGW0nDUU34tjA,493
9
9
  params_proto/cli/ansi_help.py,sha256=-1gzbvOpi9GjPlqgiINOYQAfIstzg0-ukv1se88TYCQ,10967
@@ -11,7 +11,7 @@ params_proto/cli/cli_parse.py,sha256=aRXkPdfyRFJeXb6W6NwQ1bkkkhjUQlvin1k4GaUtbFg
11
11
  params_proto/cli/help_gen.py,sha256=Iv9MWC7TJT4_OUWozTfCr8-Nmp_-K8Ohoim_dtsN5AY,12921
12
12
  params_proto/hyper/__init__.py,sha256=4zMnKk9H7NPlaTTRzbL2MC7anzwkBbd2_kW51aYhCPs,157
13
13
  params_proto/hyper/proxies.py,sha256=OMiaKK-gQx-zT1xeCmZevBSDgWUwwkzz0n54A5_wC60,4492
14
- params_proto/hyper/sweep.py,sha256=U9xpJ4o3_jlGMUkcW9YrGE9VvYeld91AOLjkYGnDSMc,28604
14
+ params_proto/hyper/sweep.py,sha256=F6uac1o7_zzahgvnwcuqwn-7C48Xl-RlHKsrk6wulsU,29475
15
15
  params_proto/v1/__init__.py,sha256=NGYZ6Iqicc5M6iyWT6N8FsD0iGLl2by5yZUIsHKhjXw,48
16
16
  params_proto/v1/hyper.py,sha256=zFzViWtSkQdqDJXuan33X2OZwKSHHY39Q5HSNPXl0iQ,2883
17
17
  params_proto/v1/params_proto.py,sha256=g2TMTG0SXyp01gsvd9EO42m28Hr2aS79xzOnMeH_WVk,8728
@@ -20,7 +20,7 @@ params_proto/v2/hyper.py,sha256=onBAkT8Ja8IkeHEOq1AwCdTuBzAnthIe766ZE0lAy-M,1146
20
20
  params_proto/v2/partial.py,sha256=_ovi4NY8goYgHurfYt1OV0E9DSMXGYucjMVIyG1Q_xc,983
21
21
  params_proto/v2/proto.py,sha256=KvinzgzwRQr2bHDNtrU7App2kgAyB-SEfBe4SNYceh0,18995
22
22
  params_proto/v2/utils.py,sha256=5EWvwboZDTsCYfzSED_J6RVFyNLIlf95nIu4p_ZSVxA,3540
23
- params_proto-3.0.0rc24.dist-info/METADATA,sha256=YjoFx1VC6zfVyvnGjAnBpuoQO8dY6ELw9xcZaZSNdmI,8994
24
- params_proto-3.0.0rc24.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
25
- params_proto-3.0.0rc24.dist-info/licenses/LICENSE.md,sha256=c2qSYi9tUMZtzj9SEsMeKhub5LJUmHwBtDLiIMM5b6U,1526
26
- params_proto-3.0.0rc24.dist-info/RECORD,,
23
+ params_proto-3.0.0rc26.dist-info/METADATA,sha256=8k3wrzgdJnM0Oh_8lw9sBJmlmKusAVZgjg3kx_WhYIw,8995
24
+ params_proto-3.0.0rc26.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
25
+ params_proto-3.0.0rc26.dist-info/licenses/LICENSE.md,sha256=c2qSYi9tUMZtzj9SEsMeKhub5LJUmHwBtDLiIMM5b6U,1526
26
+ params_proto-3.0.0rc26.dist-info/RECORD,,