params-proto 3.2.1__tar.gz → 3.2.3__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.
Files changed (133) hide show
  1. {params_proto-3.2.1 → params_proto-3.2.3}/.idea/workspace.xml +4 -2
  2. {params_proto-3.2.1 → params_proto-3.2.3}/PKG-INFO +1 -2
  3. {params_proto-3.2.1 → params_proto-3.2.3}/docs/release_notes.md +30 -0
  4. {params_proto-3.2.1 → params_proto-3.2.3}/pyproject.toml +1 -2
  5. params_proto-3.2.3/scratch/test_super.py +146 -0
  6. params_proto-3.2.3/scratch/test_super_minimal.py +53 -0
  7. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/proto.py +28 -7
  8. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/v1/params_proto.py +7 -1
  9. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/v2/proto.py +7 -1
  10. params_proto-3.2.3/tests/test_v3/test_proto_context_manager.py +179 -0
  11. {params_proto-3.2.1 → params_proto-3.2.3}/.claude-plugin/marketplace.json +0 -0
  12. {params_proto-3.2.1 → params_proto-3.2.3}/.claude-plugin/plugin.json +0 -0
  13. {params_proto-3.2.1 → params_proto-3.2.3}/.editorconfig +0 -0
  14. {params_proto-3.2.1 → params_proto-3.2.3}/.gitignore +0 -0
  15. {params_proto-3.2.1 → params_proto-3.2.3}/.idea/.gitignore +0 -0
  16. {params_proto-3.2.1 → params_proto-3.2.3}/.idea/codeStyles/codeStyleConfig.xml +0 -0
  17. {params_proto-3.2.1 → params_proto-3.2.3}/.idea/inspectionProfiles/Project_Default.xml +0 -0
  18. {params_proto-3.2.1 → params_proto-3.2.3}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  19. {params_proto-3.2.1 → params_proto-3.2.3}/.idea/markdown.xml +0 -0
  20. {params_proto-3.2.1 → params_proto-3.2.3}/.idea/misc.xml +0 -0
  21. {params_proto-3.2.1 → params_proto-3.2.3}/.idea/modules.xml +0 -0
  22. {params_proto-3.2.1 → params_proto-3.2.3}/.idea/params-proto.iml +0 -0
  23. {params_proto-3.2.1 → params_proto-3.2.3}/.idea/ruff.xml +0 -0
  24. {params_proto-3.2.1 → params_proto-3.2.3}/.idea/vcs.xml +0 -0
  25. {params_proto-3.2.1 → params_proto-3.2.3}/.readthedocs.yaml +0 -0
  26. {params_proto-3.2.1 → params_proto-3.2.3}/.run/pytest for test_neo_proto_cli.run.xml +0 -0
  27. {params_proto-3.2.1 → params_proto-3.2.3}/.run/pytest in test_params_proto.run.xml +0 -0
  28. {params_proto-3.2.1 → params_proto-3.2.3}/ANSI_HELP_CONSIDERATIONS.md +0 -0
  29. {params_proto-3.2.1 → params_proto-3.2.3}/CLAUDE.md +0 -0
  30. {params_proto-3.2.1 → params_proto-3.2.3}/LICENSE.md +0 -0
  31. {params_proto-3.2.1 → params_proto-3.2.3}/Makefile +0 -0
  32. {params_proto-3.2.1 → params_proto-3.2.3}/README +0 -0
  33. {params_proto-3.2.1 → params_proto-3.2.3}/README.md +0 -0
  34. {params_proto-3.2.1 → params_proto-3.2.3}/docs/Makefile +0 -0
  35. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_archive_v2/api/hyper.md +0 -0
  36. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_archive_v2/api/proto.md +0 -0
  37. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_archive_v2/api/utils.md +0 -0
  38. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_archive_v2/examples/advanced_features.md +0 -0
  39. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_archive_v2/examples/basic_usage.md +0 -0
  40. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_archive_v2/examples/environment_variables.md +0 -0
  41. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_archive_v2/examples/hyperparameter_sweeps.md +0 -0
  42. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_archive_v2/examples/index.md +0 -0
  43. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_archive_v2/examples/nested_configs.md +0 -0
  44. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_archive_v2/quick_start.md +0 -0
  45. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_static/ansi.css +0 -0
  46. {params_proto-3.2.1 → params_proto-3.2.3}/docs/_static/custom.css +0 -0
  47. {params_proto-3.2.1 → params_proto-3.2.3}/docs/api/index.md +0 -0
  48. {params_proto-3.2.1 → params_proto-3.2.3}/docs/api/proto.md +0 -0
  49. {params_proto-3.2.1 → params_proto-3.2.3}/docs/conf.py +0 -0
  50. {params_proto-3.2.1 → params_proto-3.2.3}/docs/examples/basic_usage.md +0 -0
  51. {params_proto-3.2.1 → params_proto-3.2.3}/docs/examples/cli_applications.md +0 -0
  52. {params_proto-3.2.1 → params_proto-3.2.3}/docs/examples/ml_training.md +0 -0
  53. {params_proto-3.2.1 → params_proto-3.2.3}/docs/examples/rl_agent.md +0 -0
  54. {params_proto-3.2.1 → params_proto-3.2.3}/docs/index.md +0 -0
  55. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/advanced_patterns.md +0 -0
  56. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/ansi_formatting.md +0 -0
  57. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/cli-fundamentals.md +0 -0
  58. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/cli-patterns.md +0 -0
  59. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/configuration-patterns.md +0 -0
  60. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/core-concepts.md +0 -0
  61. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/environment_variables.md +0 -0
  62. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/help-generation.md +0 -0
  63. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/hyperparameter_sweeps.md +0 -0
  64. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/naming-conventions.md +0 -0
  65. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/parameter-iteration.md +0 -0
  66. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/parameter-overrides.md +0 -0
  67. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/type-system.md +0 -0
  68. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/union_types.md +0 -0
  69. {params_proto-3.2.1 → params_proto-3.2.3}/docs/key_concepts/welcome.md +0 -0
  70. {params_proto-3.2.1 → params_proto-3.2.3}/docs/migration.md +0 -0
  71. {params_proto-3.2.1 → params_proto-3.2.3}/docs/quick_start.md +0 -0
  72. {params_proto-3.2.1 → params_proto-3.2.3}/docs/requirements.txt +0 -0
  73. {params_proto-3.2.1 → params_proto-3.2.3}/examples/union_subcommands.py +0 -0
  74. {params_proto-3.2.1 → params_proto-3.2.3}/figures/man-page.png +0 -0
  75. {params_proto-3.2.1 → params_proto-3.2.3}/figures/params-proto-autocompletion.gif +0 -0
  76. {params_proto-3.2.1 → params_proto-3.2.3}/figures/spec_files.png +0 -0
  77. {params_proto-3.2.1 → params_proto-3.2.3}/scratch/demo_params_proto.py +0 -0
  78. {params_proto-3.2.1 → params_proto-3.2.3}/scratch/demo_v3.py +0 -0
  79. {params_proto-3.2.1 → params_proto-3.2.3}/scratch/proto_DAT_scratch.py +0 -0
  80. {params_proto-3.2.1 → params_proto-3.2.3}/scratch/proto_dependency_tree_pattern.py +0 -0
  81. {params_proto-3.2.1 → params_proto-3.2.3}/skills/params-proto/SKILL.md +0 -0
  82. {params_proto-3.2.1 → params_proto-3.2.3}/skills/params-proto/references/cli-and-types.md +0 -0
  83. {params_proto-3.2.1 → params_proto-3.2.3}/skills/params-proto/references/environment-vars.md +0 -0
  84. {params_proto-3.2.1 → params_proto-3.2.3}/skills/params-proto/references/patterns.md +0 -0
  85. {params_proto-3.2.1 → params_proto-3.2.3}/skills/params-proto/references/sweeps.md +0 -0
  86. {params_proto-3.2.1 → params_proto-3.2.3}/skills/params-proto.skill +0 -0
  87. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/__init__.py +0 -0
  88. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/app.py +0 -0
  89. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/cli/__init__.py +0 -0
  90. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/cli/ansi_help.py +0 -0
  91. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/cli/cli_parse.py +0 -0
  92. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/cli/help_gen.py +0 -0
  93. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/documentation.py +0 -0
  94. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/envvar.py +0 -0
  95. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/hyper/__init__.py +0 -0
  96. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/hyper/proxies.py +0 -0
  97. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/hyper/sweep.py +0 -0
  98. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/parse_env_template.py +0 -0
  99. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/type_utils.py +0 -0
  100. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/v1/__init__.py +0 -0
  101. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/v1/hyper.py +0 -0
  102. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/v2/__init__.py +0 -0
  103. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/v2/hyper.py +0 -0
  104. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/v2/partial.py +0 -0
  105. {params_proto-3.2.1 → params_proto-3.2.3}/src/params_proto/v2/utils.py +0 -0
  106. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v1/__init__.py +0 -0
  107. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v1/test_hyper.py +0 -0
  108. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v1/test_params_proto.py +0 -0
  109. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v2/test_Eval.py +0 -0
  110. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v2/test_neo_hyper.py +0 -0
  111. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v2/test_neo_proto.py +0 -0
  112. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v2/test_neo_proto_cli.py +0 -0
  113. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v2/test_neo_proto_partial.py +0 -0
  114. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v2/test_utils.py +0 -0
  115. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/conftest.py +0 -0
  116. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/samples/train.py +0 -0
  117. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_advanced_types.py +0 -0
  118. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_class_level_methods.py +0 -0
  119. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_cli_parsing.py +0 -0
  120. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_help_strings.py +0 -0
  121. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_method_self_param.py +0 -0
  122. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_nested_cli.py +0 -0
  123. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_parse_env_template.py +0 -0
  124. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_piter.py +0 -0
  125. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_positional_example.sh +0 -0
  126. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_proto_comments.py +0 -0
  127. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_proto_core.py +0 -0
  128. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_proto_envvar.py +0 -0
  129. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_proto_linebreaking.py +0 -0
  130. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_proto_partial.py +0 -0
  131. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_proto_required.py +0 -0
  132. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_strings.py +0 -0
  133. {params_proto-3.2.1 → params_proto-3.2.3}/tests/test_v3/test_sweep.py +0 -0
@@ -257,7 +257,9 @@
257
257
  <workItem from="1768382679094" duration="3000" />
258
258
  <workItem from="1769135684123" duration="4247000" />
259
259
  <workItem from="1769569263076" duration="1863000" />
260
- <workItem from="1769933004821" duration="3502000" />
260
+ <workItem from="1769933004821" duration="4284000" />
261
+ <workItem from="1770101044881" duration="1209000" />
262
+ <workItem from="1770110071925" duration="622000" />
261
263
  </task>
262
264
  <task id="LOCAL-00001" summary="add design specs">
263
265
  <option name="closed" value="true" />
@@ -298,7 +300,7 @@
298
300
  <SUITE FILE_PATH="coverage/params_proto$scratch.coverage" NAME="scratch Coverage Results" MODIFIED="1762422882594" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
299
301
  <SUITE FILE_PATH="coverage/params_proto$demo_params_proto.coverage" NAME="demo_params_proto Coverage Results" MODIFIED="1762230413342" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/scratch" />
300
302
  <SUITE FILE_PATH="coverage/params_proto$pytest_for_tests_test_v3_test_proto_cli_test_proto_cli.coverage" NAME="pytest for tests.test_v3.test_proto_cli.test_proto_cli Coverage Results" MODIFIED="1762386112096" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
301
- <SUITE FILE_PATH="coverage/params_proto$pytest_in_tests.coverage" NAME="pytest in tests Coverage Results" MODIFIED="1769934711414" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/tests" />
303
+ <SUITE FILE_PATH="coverage/params_proto$pytest_in_tests.coverage" NAME="pytest in tests Coverage Results" MODIFIED="1769937166592" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/tests" />
302
304
  <SUITE FILE_PATH="coverage/params_proto$pytest_in_test_parse_env_template_py.coverage" NAME="pytest in test_parse_env_template.py Coverage Results" MODIFIED="1762242493577" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
303
305
  <SUITE FILE_PATH="coverage/params_proto$pytest_in_test_v2.coverage" NAME="pytest in test_v2 Coverage Results" MODIFIED="1762232660046" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/tests/test_v2" />
304
306
  <SUITE FILE_PATH="coverage/params_proto$pytest_in_test_neo_proto_py.coverage" NAME="pytest in test_neo_proto.py Coverage Results" MODIFIED="1762233469671" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: params-proto
3
- Version: 3.2.1
3
+ Version: 3.2.3
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
@@ -23,7 +23,6 @@ Requires-Dist: argcomplete
23
23
  Requires-Dist: argparse
24
24
  Requires-Dist: expandvars
25
25
  Requires-Dist: termcolor
26
- Requires-Dist: waterbear>=2.6.8
27
26
  Provides-Extra: dev
28
27
  Requires-Dist: pandas; extra == 'dev'
29
28
  Requires-Dist: pytest; extra == 'dev'
@@ -2,6 +2,36 @@
2
2
 
3
3
  This page contains the release history and changelog for params-proto.
4
4
 
5
+ ## Version 3.2.3 (2025-02-04)
6
+
7
+ ### 🐛 Bug Fixes
8
+
9
+ - **Context Manager Protocol**: Fixed regression where `__enter__` and `__exit__` methods were stripped from `@proto` and `@proto.prefix` decorated classes
10
+ - Classes implementing context managers now work correctly with `with` statements
11
+ - Also preserves other user-defined protocol methods: `__call__`, `__iter__`, `__next__`, `__getitem__`, `__len__`, etc.
12
+ - Root cause: method copying skipped all dunder methods, but should preserve user-defined ones
13
+
14
+ ### 🧪 Testing
15
+
16
+ - Added comprehensive tests for context manager and other Python protocol preservation
17
+
18
+ ---
19
+
20
+ ## Version 3.2.2 (2025-02-03)
21
+
22
+ ### 🐛 Bug Fixes
23
+
24
+ - **`super()` in `@proto` Methods**: Fixed `super()` calls failing with `TypeError: super(type, obj): obj must be an instance or subtype of type`
25
+ - Methods using `super()` in `@proto` or `@proto.prefix` decorated classes now work correctly
26
+ - Root cause: decorated class was recreated as sibling instead of subclass of original
27
+ - Fix: decorated class is now a proper subclass, preserving the MRO chain
28
+
29
+ - **`@proto` Inheriting from `@proto`**: Fixed `TypeError: duplicate base class ptype` when a `@proto` class inherits from another `@proto` class
30
+ - Nested `@proto` inheritance now works correctly
31
+ - Metaclass merging skips duplicate `ptype` bases
32
+
33
+ ---
34
+
5
35
  ## Version 3.2.1 (2025-02-01)
6
36
 
7
37
  ### 🐛 Bug Fixes
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "params-proto"
3
- version = "3.2.1"
3
+ version = "3.2.3"
4
4
  description = "Modern Hyper Parameter Management for Machine Learning"
5
5
  authors = [
6
6
  { name = "Ge Yang" }
@@ -32,7 +32,6 @@ classifiers = [
32
32
  ]
33
33
 
34
34
  dependencies = [
35
- "waterbear>=2.6.8",
36
35
  "argparse",
37
36
  "argcomplete",
38
37
  "expandvars",
@@ -0,0 +1,146 @@
1
+ """Test super() calls inside @proto and @proto.prefix decorated classes."""
2
+
3
+ from params_proto import proto
4
+
5
+
6
+ # Test 1: Basic inheritance with @proto
7
+ class BaseClass:
8
+ base_value: int = 10
9
+
10
+ def base_method(self):
11
+ return f"base: {self.base_value}"
12
+
13
+
14
+ @proto
15
+ class ChildProto(BaseClass):
16
+ child_value: int = 20
17
+
18
+ def child_method(self):
19
+ base_result = super().base_method()
20
+ return f"{base_result}, child: {self.child_value}"
21
+
22
+
23
+ # Test 2: Inheritance with @proto.prefix
24
+ class BaseConfig:
25
+ lr: float = 0.01
26
+
27
+ def describe(self):
28
+ return f"lr={self.lr}"
29
+
30
+
31
+ @proto.prefix
32
+ class ChildConfig(BaseConfig):
33
+ batch_size: int = 32
34
+
35
+ def describe(self):
36
+ base = super().describe()
37
+ return f"{base}, batch_size={self.batch_size}"
38
+
39
+
40
+ # Test 3: Inheritance between two @proto decorated classes
41
+ @proto
42
+ class ParentProto:
43
+ parent_attr: str = "parent"
44
+
45
+ def get_info(self):
46
+ return f"Parent: {self.parent_attr}"
47
+
48
+
49
+ @proto
50
+ class GrandchildProto(ParentProto):
51
+ grandchild_attr: str = "grandchild"
52
+
53
+ def get_info(self):
54
+ parent_info = super().get_info()
55
+ return f"{parent_info}, Grandchild: {self.grandchild_attr}"
56
+
57
+
58
+ # Test 4: __post_init__ with super() (undecorated base)
59
+ class BaseWithInit:
60
+ value: int = 1
61
+ processed: bool = False
62
+
63
+ def __post_init__(self):
64
+ self.processed = True
65
+
66
+
67
+ @proto
68
+ class ChildWithInit(BaseWithInit):
69
+ multiplier: int = 2
70
+ child_processed: bool = False
71
+
72
+ def __post_init__(self):
73
+ super().__post_init__()
74
+ self.child_processed = True
75
+ self.value *= self.multiplier
76
+
77
+
78
+ def main():
79
+ print("=" * 60)
80
+ print("Testing super() in @proto and @proto.prefix classes")
81
+ print("=" * 60)
82
+
83
+ # Test 1
84
+ print("\n--- Test 1: Basic inheritance with @proto ---")
85
+ try:
86
+ child = ChildProto()
87
+ print(f"child class: {child.__class__}")
88
+ print(f"child MRO: {child.__class__.__mro__}")
89
+ print(f"ChildProto: {ChildProto}")
90
+ print(f"ChildProto MRO: {ChildProto.__mro__}")
91
+ print(f"isinstance(child, ChildProto): {isinstance(child, ChildProto)}")
92
+ print(f"isinstance(child, BaseClass): {isinstance(child, BaseClass)}")
93
+ result = child.child_method()
94
+ print(f"Result: {result}")
95
+ assert "base: 10" in result, f"Expected 'base: 10' in result"
96
+ assert "child: 20" in result, f"Expected 'child: 20' in result"
97
+ print("✓ PASSED")
98
+ except Exception as e:
99
+ import traceback
100
+ print(f"✗ FAILED: {e}")
101
+ traceback.print_exc()
102
+
103
+ # Test 2
104
+ print("\n--- Test 2: Inheritance with @proto.prefix ---")
105
+ try:
106
+ config = ChildConfig()
107
+ result = config.describe()
108
+ print(f"Result: {result}")
109
+ assert "lr=" in result, f"Expected 'lr=' in result"
110
+ assert "batch_size=" in result, f"Expected 'batch_size=' in result"
111
+ print("✓ PASSED")
112
+ except Exception as e:
113
+ print(f"✗ FAILED: {e}")
114
+
115
+ # Test 3
116
+ print("\n--- Test 3: Inheritance between @proto classes ---")
117
+ try:
118
+ grandchild = GrandchildProto()
119
+ result = grandchild.get_info()
120
+ print(f"Result: {result}")
121
+ assert "Parent:" in result, f"Expected 'Parent:' in result"
122
+ assert "Grandchild:" in result, f"Expected 'Grandchild:' in result"
123
+ print("✓ PASSED")
124
+ except Exception as e:
125
+ import traceback
126
+ print(f"✗ FAILED: {e}")
127
+ traceback.print_exc()
128
+
129
+ # Test 4
130
+ print("\n--- Test 4: __post_init__ with super() ---")
131
+ try:
132
+ obj = ChildWithInit()
133
+ print(f"value={obj.value}, processed={obj.processed}, child_processed={obj.child_processed}")
134
+ assert obj.processed, "Expected processed=True from parent __post_init__"
135
+ assert obj.child_processed, "Expected child_processed=True from child __post_init__"
136
+ assert obj.value == 2, f"Expected value=2 (1*2), got {obj.value}"
137
+ print("✓ PASSED")
138
+ except Exception as e:
139
+ print(f"✗ FAILED: {e}")
140
+
141
+ print("\n" + "=" * 60)
142
+ print("All tests completed!")
143
+
144
+
145
+ if __name__ == "__main__":
146
+ main()
@@ -0,0 +1,53 @@
1
+ """Minimal test for super() issue."""
2
+
3
+ from params_proto import proto
4
+
5
+
6
+ class Base:
7
+ def method(self):
8
+ return "base"
9
+
10
+
11
+ @proto
12
+ class Child(Base):
13
+ attr: int = 1
14
+
15
+ def method(self):
16
+ # This should call Base.method()
17
+ return f"child + {super().method()}"
18
+
19
+
20
+ # Test without instantiation
21
+ print("Child class:", Child)
22
+ print("Child id:", id(Child))
23
+ print("Child.__mro__:", Child.__mro__)
24
+ print("Child.__proto_original_class__:", Child.__proto_original_class__)
25
+ print("Original class id:", id(Child.__proto_original_class__))
26
+ print("Same class?", Child is Child.__proto_original_class__)
27
+
28
+ # Test instantiation
29
+ print("\nCreating instance...")
30
+ child = Child()
31
+ print("Instance class:", child.__class__)
32
+ print("Instance type:", type(child))
33
+
34
+ # Check what method is on the instance
35
+ print("\nMethod on instance:", child.method)
36
+
37
+ # Try calling
38
+ print("\nCalling method...")
39
+ try:
40
+ result = child.method()
41
+ print(f"Result: {result}")
42
+ except Exception as e:
43
+ print(f"Error: {e}")
44
+
45
+ # Debug: manually call the original method
46
+ print("\nDebug: Trying to call original method directly...")
47
+ original_cls = Child.__proto_original_class__
48
+ print(f"Original class: {original_cls}")
49
+ print(f"Original method: {original_cls.method}")
50
+
51
+ # Create instance via original class
52
+ from params_proto.proto import _SINGLETONS
53
+ print(f"\nSINGLETONS: {_SINGLETONS}")
@@ -698,10 +698,23 @@ class ptype(type):
698
698
 
699
699
  # Copy methods from original class and wrap to return self
700
700
  for name in dir(original_cls):
701
- # Skip dunder methods and proto fields (fields are handled above)
702
- if name.startswith("__") or name in annotations:
701
+ # Skip proto fields (fields are handled above)
702
+ if name in annotations:
703
703
  continue
704
704
 
705
+ # For dunder methods, only copy user-defined ones (not inherited from object/type)
706
+ if name.startswith("__"):
707
+ # Check if this dunder method is user-defined (not from object or type)
708
+ is_user_defined = False
709
+ for klass in original_cls.__mro__:
710
+ if klass is object or klass is type:
711
+ break
712
+ if name in klass.__dict__:
713
+ is_user_defined = True
714
+ break
715
+ if not is_user_defined:
716
+ continue
717
+
705
718
  # Check raw descriptor in MRO to detect staticmethod/classmethod (handles inheritance)
706
719
  raw_attr = None
707
720
  for klass in original_cls.__mro__:
@@ -844,10 +857,15 @@ def proto(
844
857
  # Handle existing metaclass
845
858
  existing_meta = type(obj)
846
859
  if existing_meta is not type:
847
- # Merge with existing metaclass
848
- class MergedMeta(existing_meta, ptype):
849
- pass
850
- metaclass = MergedMeta
860
+ # Check if existing metaclass is already ptype or a subclass of ptype
861
+ if issubclass(existing_meta, ptype):
862
+ # Already using ptype, no need to merge
863
+ metaclass = existing_meta
864
+ else:
865
+ # Merge with existing metaclass
866
+ class MergedMeta(existing_meta, ptype):
867
+ pass
868
+ metaclass = MergedMeta
851
869
  else:
852
870
  metaclass = ptype
853
871
 
@@ -872,9 +890,12 @@ def proto(
872
890
  namespace[key] = value
873
891
 
874
892
  # Create new class with metaclass
893
+ # IMPORTANT: Use (obj,) as bases to make new class a SUBCLASS of original.
894
+ # This ensures super() works correctly - the original class is in the MRO,
895
+ # so Python's super() validation passes when checking isinstance(self, original_class).
875
896
  new_cls = metaclass(
876
897
  obj.__name__,
877
- obj.__bases__,
898
+ (obj,),
878
899
  namespace
879
900
  )
880
901
 
@@ -17,7 +17,13 @@ import inspect
17
17
  import re
18
18
  from typing import Callable, TypeVar, Union
19
19
 
20
- from waterbear import DefaultBear
20
+ try:
21
+ from waterbear import DefaultBear
22
+ except ImportError:
23
+ raise ImportError(
24
+ "params_proto.v1 requires waterbear. Install it with: pip install waterbear\n"
25
+ "Note: The v3 API (from params_proto import proto) does not require waterbear."
26
+ )
21
27
 
22
28
 
23
29
  def _strtobool(val):
@@ -8,7 +8,13 @@ from types import BuiltinFunctionType, SimpleNamespace
8
8
  from warnings import warn
9
9
 
10
10
  from expandvars import expandvars
11
- from waterbear import Bear
11
+ try:
12
+ from waterbear import Bear
13
+ except ImportError:
14
+ raise ImportError(
15
+ "params_proto.v2 requires waterbear. Install it with: pip install waterbear\n"
16
+ "Note: The v3 API (from params_proto import proto) does not require waterbear."
17
+ )
12
18
 
13
19
  from params_proto.parse_env_template import all_available
14
20
  from params_proto.v2.utils import dot_to_deps
@@ -0,0 +1,179 @@
1
+ """Test that @proto and @proto.prefix preserve context manager protocol."""
2
+
3
+ import pytest
4
+ from params_proto import proto
5
+
6
+
7
+ class TestContextManager:
8
+ """Test context manager protocol preservation."""
9
+
10
+ def test_proto_prefix_context_manager(self):
11
+ """Test that @proto.prefix preserves __enter__ and __exit__."""
12
+
13
+ @proto.prefix
14
+ class RUN:
15
+ prefix: str = "test"
16
+ entered: bool = False
17
+ exited: bool = False
18
+
19
+ def __enter__(self):
20
+ self.entered = True
21
+ return self
22
+
23
+ def __exit__(self, exc_type, exc_val, exc_tb):
24
+ self.exited = True
25
+ return False
26
+
27
+ run = RUN()
28
+
29
+ # Check methods exist
30
+ assert hasattr(run, "__enter__"), "Should have __enter__ method"
31
+ assert hasattr(run, "__exit__"), "Should have __exit__ method"
32
+
33
+ # Test actual context manager usage
34
+ with run as r:
35
+ assert r.entered, "Should have called __enter__"
36
+ assert not r.exited, "Should not have called __exit__ yet"
37
+
38
+ assert run.exited, "Should have called __exit__"
39
+
40
+ def test_proto_context_manager(self):
41
+ """Test that @proto preserves __enter__ and __exit__."""
42
+
43
+ @proto
44
+ class Session:
45
+ name: str = "session"
46
+ active: bool = False
47
+
48
+ def __enter__(self):
49
+ self.active = True
50
+ return self
51
+
52
+ def __exit__(self, exc_type, exc_val, exc_tb):
53
+ self.active = False
54
+ return False
55
+
56
+ session = Session()
57
+
58
+ with session as s:
59
+ assert s.active, "Should be active inside context"
60
+
61
+ assert not session.active, "Should be inactive after context"
62
+
63
+ def test_context_manager_with_exception(self):
64
+ """Test context manager handles exceptions correctly."""
65
+
66
+ @proto.prefix
67
+ class RUN:
68
+ prefix: str = "test"
69
+ exc_info: tuple = None
70
+
71
+ def __enter__(self):
72
+ return self
73
+
74
+ def __exit__(self, exc_type, exc_val, exc_tb):
75
+ self.exc_info = (exc_type, exc_val, exc_tb)
76
+ return True # Suppress exception
77
+
78
+ run = RUN()
79
+
80
+ with run:
81
+ raise ValueError("test error")
82
+
83
+ # Exception should have been captured
84
+ assert run.exc_info[0] is ValueError
85
+ assert str(run.exc_info[1]) == "test error"
86
+
87
+ def test_context_manager_inheritance(self):
88
+ """Test context manager works with inheritance."""
89
+
90
+ class BaseContext:
91
+ def __enter__(self):
92
+ self.entered = True
93
+ return self
94
+
95
+ def __exit__(self, exc_type, exc_val, exc_tb):
96
+ self.exited = True
97
+ return False
98
+
99
+ @proto.prefix
100
+ class ChildRUN(BaseContext):
101
+ prefix: str = "child"
102
+ entered: bool = False
103
+ exited: bool = False
104
+
105
+ run = ChildRUN()
106
+
107
+ with run as r:
108
+ assert r.entered
109
+
110
+ assert run.exited
111
+
112
+
113
+ class TestOtherProtocols:
114
+ """Test other Python protocol methods are preserved."""
115
+
116
+ def test_callable_protocol(self):
117
+ """Test __call__ is preserved."""
118
+
119
+ @proto.prefix
120
+ class Callable:
121
+ value: int = 0
122
+ call_count: int = 0
123
+
124
+ def __call__(self, x):
125
+ self.call_count += 1
126
+ return self.value + x
127
+
128
+ c = Callable()
129
+ c.value = 10
130
+
131
+ result = c(5)
132
+ assert result == 15
133
+ assert c.call_count == 1
134
+
135
+ def test_iterator_protocol(self):
136
+ """Test __iter__ and __next__ are preserved."""
137
+
138
+ @proto.prefix
139
+ class Counter:
140
+ start: int = 0
141
+ end: int = 3
142
+ _current: int = 0
143
+
144
+ def __iter__(self):
145
+ self._current = self.start
146
+ return self
147
+
148
+ def __next__(self):
149
+ if self._current >= self.end:
150
+ raise StopIteration
151
+ value = self._current
152
+ self._current += 1
153
+ return value
154
+
155
+ counter = Counter()
156
+ result = list(counter)
157
+ assert result == [0, 1, 2]
158
+
159
+ def test_container_protocol(self):
160
+ """Test __getitem__ and __len__ are preserved."""
161
+
162
+ @proto.prefix
163
+ class Container:
164
+ items: list = None
165
+
166
+ def __post_init__(self):
167
+ if self.items is None:
168
+ self.items = [1, 2, 3]
169
+
170
+ def __getitem__(self, index):
171
+ return self.items[index]
172
+
173
+ def __len__(self):
174
+ return len(self.items)
175
+
176
+ c = Container()
177
+ assert len(c) == 3
178
+ assert c[0] == 1
179
+ assert c[1] == 2
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