python-postman 0.9.0__tar.gz → 0.9.1__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 (169) hide show
  1. {python_postman-0.9.0 → python_postman-0.9.1}/PKG-INFO +41 -30
  2. {python_postman-0.9.0 → python_postman-0.9.1}/README.md +32 -22
  3. python_postman-0.9.1/audit.md +147 -0
  4. {python_postman-0.9.0 → python_postman-0.9.1}/docs/README.md +12 -14
  5. {python_postman-0.9.0 → python_postman-0.9.1}/docs/architecture/execution-layer.md +74 -99
  6. {python_postman-0.9.0 → python_postman-0.9.1}/docs/architecture/layer-interaction.md +20 -29
  7. {python_postman-0.9.0 → python_postman-0.9.1}/docs/architecture/model-layer.md +8 -11
  8. {python_postman-0.9.0 → python_postman-0.9.1}/docs/architecture/overview.md +4 -3
  9. {python_postman-0.9.0 → python_postman-0.9.1}/docs/examples/generate-documentation.py +1 -2
  10. {python_postman-0.9.0 → python_postman-0.9.1}/docs/examples/parse-inspect-modify-execute.py +27 -33
  11. {python_postman-0.9.0 → python_postman-0.9.1}/docs/guides/decision-tree.md +13 -19
  12. {python_postman-0.9.0 → python_postman-0.9.1}/docs/guides/description-fields.md +6 -8
  13. {python_postman-0.9.0 → python_postman-0.9.1}/docs/guides/optional-dependencies.md +7 -14
  14. {python_postman-0.9.0 → python_postman-0.9.1}/docs/guides/troubleshooting.md +17 -20
  15. python_postman-0.9.1/docs/usage.md +1772 -0
  16. {python_postman-0.9.0 → python_postman-0.9.1}/examples/advanced_execution.py +1 -1
  17. python_postman-0.9.1/examples/eia_api_example.py +231 -0
  18. python_postman-0.9.1/plan.md +681 -0
  19. {python_postman-0.9.0 → python_postman-0.9.1}/pyproject.toml +13 -16
  20. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/__init__.py +3 -3
  21. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/__init__.py +2 -2
  22. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/exceptions.py +1 -1
  23. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/executor.py +90 -126
  24. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/script_runner.py +151 -7
  25. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/variable_resolver.py +1 -1
  26. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/introspection/variable_tracer.py +1 -1
  27. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/auth.py +2 -20
  28. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/collection.py +52 -1
  29. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/folder.py +10 -1
  30. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/variable.py +1 -0
  31. python_postman-0.9.1/python_postman/py.typed +0 -0
  32. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/search/query.py +34 -6
  33. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/statistics/collector.py +1 -2
  34. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/types/auth_types.py +4 -0
  35. {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/test_cftc_comprehensive.py +8 -8
  36. python_postman-0.9.1/tests/test_data/real_world/EIA_APIv2.postman_collection.json +37474 -0
  37. python_postman-0.9.1/tests/test_data/real_world/Genscape_Oil_Storage.postman_collection.json +1047 -0
  38. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_execution_exceptions.py +13 -13
  39. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_executor.py +5 -5
  40. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_imports.py +4 -4
  41. python_postman-0.9.1/tests/test_real_world_collections.py +158 -0
  42. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_script_runner.py +2 -3
  43. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_type_safety.py +1 -1
  44. {python_postman-0.9.0 → python_postman-0.9.1}/uv.lock +20 -13
  45. python_postman-0.9.0/tests/test_execution_reporter.py +0 -268
  46. {python_postman-0.9.0 → python_postman-0.9.1}/.github/workflows/python-publish.yml +0 -0
  47. {python_postman-0.9.0 → python_postman-0.9.1}/.gitignore +0 -0
  48. {python_postman-0.9.0 → python_postman-0.9.1}/LICENSE +0 -0
  49. {python_postman-0.9.0 → python_postman-0.9.1}/coverage.json +0 -0
  50. {python_postman-0.9.0 → python_postman-0.9.1}/docs/examples/description-usage.py +0 -0
  51. {python_postman-0.9.0 → python_postman-0.9.1}/docs/examples/variable-introspection.py +0 -0
  52. {python_postman-0.9.0 → python_postman-0.9.1}/docs/guides/variable-scoping.md +0 -0
  53. {python_postman-0.9.0 → python_postman-0.9.1}/examples/README.md +0 -0
  54. {python_postman-0.9.0 → python_postman-0.9.1}/examples/auth_resolution_example.py +0 -0
  55. {python_postman-0.9.0 → python_postman-0.9.1}/examples/authentication_examples.py +0 -0
  56. {python_postman-0.9.0 → python_postman-0.9.1}/examples/basic_execution.py +0 -0
  57. {python_postman-0.9.0 → python_postman-0.9.1}/examples/cftc_execution_example.py +0 -0
  58. {python_postman-0.9.0 → python_postman-0.9.1}/examples/complete_workflow.py +0 -0
  59. {python_postman-0.9.0 → python_postman-0.9.1}/examples/multi_collection_execution.py +0 -0
  60. {python_postman-0.9.0 → python_postman-0.9.1}/examples/path_parameters_example.py +0 -0
  61. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/README.md +0 -0
  62. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/README.md +0 -0
  63. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/collections/auth-service.json +0 -0
  64. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/collections/order-service.json +0 -0
  65. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/collections/user-service.json +0 -0
  66. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/environments/microservices.json +0 -0
  67. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/main.py +0 -0
  68. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/README.md +0 -0
  69. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/collections/rest-api.json +0 -0
  70. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/environments/dev.json +0 -0
  71. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/environments/prod.json +0 -0
  72. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/main.py +0 -0
  73. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/requirements.txt +0 -0
  74. {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/tests/test_rest_api.py +0 -0
  75. {python_postman-0.9.0 → python_postman-0.9.1}/examples/request_convenience_methods.py +0 -0
  76. {python_postman-0.9.0 → python_postman-0.9.1}/examples/search_example.py +0 -0
  77. {python_postman-0.9.0 → python_postman-0.9.1}/examples/statistics_example.py +0 -0
  78. {python_postman-0.9.0 → python_postman-0.9.1}/examples/synchronous_execution.py +0 -0
  79. {python_postman-0.9.0 → python_postman-0.9.1}/examples/usage_patterns.py +0 -0
  80. {python_postman-0.9.0 → python_postman-0.9.1}/examples/variable_examples.py +0 -0
  81. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/__init__.py +0 -0
  82. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/base.py +0 -0
  83. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/file_error.py +0 -0
  84. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/parse_error.py +0 -0
  85. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/schema_error.py +0 -0
  86. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/validation_error.py +0 -0
  87. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/auth_handler.py +0 -0
  88. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/context.py +0 -0
  89. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/extensions.py +0 -0
  90. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/response.py +0 -0
  91. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/results.py +0 -0
  92. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/introspection/__init__.py +0 -0
  93. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/introspection/auth_resolver.py +0 -0
  94. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/__init__.py +0 -0
  95. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/body.py +0 -0
  96. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/collection_info.py +0 -0
  97. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/cookie.py +0 -0
  98. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/event.py +0 -0
  99. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/header.py +0 -0
  100. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/item.py +0 -0
  101. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/request.py +0 -0
  102. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/response.py +0 -0
  103. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/schema.py +0 -0
  104. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/url.py +0 -0
  105. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/parser.py +0 -0
  106. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/search/__init__.py +0 -0
  107. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/statistics/__init__.py +0 -0
  108. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/types/__init__.py +0 -0
  109. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/types/http_methods.py +0 -0
  110. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/utils/__init__.py +0 -0
  111. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/utils/json_parser.py +0 -0
  112. {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/utils/validators.py +0 -0
  113. {python_postman-0.9.0 → python_postman-0.9.1}/test_execution_summary.md +0 -0
  114. {python_postman-0.9.0 → python_postman-0.9.1}/tests/__init__.py +0 -0
  115. {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/CFTC_TEST_SUMMARY.md +0 -0
  116. {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/INDEX.md +0 -0
  117. {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/QUICK_START_CFTC.md +0 -0
  118. {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/README_CFTC_TESTS.md +0 -0
  119. {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/example_cftc_usage.py +0 -0
  120. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_auth.py +0 -0
  121. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_auth_handler.py +0 -0
  122. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_auth_resolver.py +0 -0
  123. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_body.py +0 -0
  124. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_collection.py +0 -0
  125. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_collection_info.py +0 -0
  126. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_cookie.py +0 -0
  127. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/README.md +0 -0
  128. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/README_integration_tests.md +0 -0
  129. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/auth_collection.json +0 -0
  130. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/auth_execution_collection.json +0 -0
  131. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/cftc.gov.postman_collection.json +0 -0
  132. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/empty_collection.json +0 -0
  133. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/error_scenarios_collection.json +0 -0
  134. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/events_collection.json +0 -0
  135. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/execution_collection.json +0 -0
  136. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/invalid_collection.json +0 -0
  137. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/malformed_json.json +0 -0
  138. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/nested_collection.json +0 -0
  139. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/nested_execution_collection.json +0 -0
  140. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/performance_collection.json +0 -0
  141. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/simple_collection.json +0 -0
  142. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_description_fields.py +0 -0
  143. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_edge_cases.py +0 -0
  144. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_edge_cases.py.bak +0 -0
  145. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_edge_cases_summary.md +0 -0
  146. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_event.py +0 -0
  147. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_exceptions.py +0 -0
  148. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_execution_context.py +0 -0
  149. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_extensions.py +0 -0
  150. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_folder.py +0 -0
  151. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_header.py +0 -0
  152. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_integration.py +0 -0
  153. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_item.py +0 -0
  154. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_item_clarity.py +0 -0
  155. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_json_parser.py +0 -0
  156. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_parser.py +0 -0
  157. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_request.py +0 -0
  158. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_request_convenience.py +0 -0
  159. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_request_execution.py +0 -0
  160. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_request_responses.py +0 -0
  161. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_response.py +0 -0
  162. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_response_cookies.py +0 -0
  163. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_schema.py +0 -0
  164. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_search.py +0 -0
  165. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_statistics.py +0 -0
  166. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_url.py +0 -0
  167. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_variable.py +0 -0
  168. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_variable_resolver.py +0 -0
  169. {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_variable_tracer.py +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-postman
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: A Python library for parsing and working with Postman collection.json files
5
- Project-URL: Homepage, https://github.com/python-postman/python-postman
6
- Project-URL: Repository, https://github.com/python-postman/python-postman
7
- Project-URL: Documentation, https://python-postman.readthedocs.io
8
- Project-URL: Bug Tracker, https://github.com/python-postman/python-postman/issues
5
+ Project-URL: Homepage, https://github.com/yudiell/python-postman
6
+ Project-URL: Repository, https://github.com/yudiell/python-postman
7
+ Project-URL: Documentation, https://github.com/yudiell/python-postman/blob/main/docs/usage.md
8
+ Project-URL: Bug Tracker, https://github.com/yudiell/python-postman/issues
9
9
  Author: Python Postman Contributors
10
10
  License: MIT
11
11
  License-File: LICENSE
@@ -15,7 +15,6 @@ Classifier: Intended Audience :: Developers
15
15
  Classifier: License :: OSI Approved :: MIT License
16
16
  Classifier: Operating System :: OS Independent
17
17
  Classifier: Programming Language :: Python :: 3
18
- Classifier: Programming Language :: Python :: 3.8
19
18
  Classifier: Programming Language :: Python :: 3.9
20
19
  Classifier: Programming Language :: Python :: 3.10
21
20
  Classifier: Programming Language :: Python :: 3.11
@@ -25,7 +24,8 @@ Classifier: Topic :: Internet :: WWW/HTTP
25
24
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
25
  Classifier: Topic :: Software Development :: Testing
27
26
  Requires-Python: >=3.9
28
- Requires-Dist: httpx>=0.28.1
27
+ Provides-Extra: all
28
+ Requires-Dist: httpx>=0.28.1; extra == 'all'
29
29
  Provides-Extra: dev
30
30
  Requires-Dist: black>=23.0.0; extra == 'dev'
31
31
  Requires-Dist: isort>=5.12.0; extra == 'dev'
@@ -33,12 +33,15 @@ Requires-Dist: mypy>=1.0.0; extra == 'dev'
33
33
  Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
34
34
  Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
35
35
  Requires-Dist: pytest>=8.4.2; extra == 'dev'
36
+ Requires-Dist: python-dotenv>=1.0.0; extra == 'dev'
36
37
  Provides-Extra: execution
37
- Requires-Dist: python-dotenv>=1.0.0; extra == 'execution'
38
+ Requires-Dist: httpx>=0.28.1; extra == 'execution'
38
39
  Description-Content-Type: text/markdown
39
40
 
40
41
  # Python Postman
41
42
 
43
+ > **Disclaimer:** This is an independent, community-maintained open-source project. It is not affiliated with, endorsed by, or sponsored by Postman, Inc. "Postman" is a registered trademark of Postman, Inc.
44
+
42
45
  A comprehensive Python library for working with Postman collections. Parse, execute, search, and analyze Postman collection.json files with a clean, object-oriented interface. Execute HTTP requests with full async/sync support, dynamic variable resolution, and authentication handling.
43
46
 
44
47
  ## Features
@@ -181,11 +184,11 @@ if collection.auth:
181
184
 
182
185
  # Access auth details based on type
183
186
  if collection.auth.type == "bearer":
184
- token = collection.auth.bearer.get("token")
187
+ token = collection.auth.get_bearer_token()
185
188
  print(f"Bearer Token: {token}")
186
189
  elif collection.auth.type == "basic":
187
- username = collection.auth.basic.get("username")
188
- print(f"Basic Auth Username: {username}")
190
+ credentials = collection.auth.get_basic_credentials()
191
+ print(f"Basic Auth Username: {credentials['username']}")
189
192
 
190
193
  # Request-level auth (overrides collection auth)
191
194
  for request in collection.get_requests():
@@ -202,13 +205,14 @@ for event in collection.events:
202
205
  print(f"Script Content: {event.script}")
203
206
 
204
207
  # Access script content from request-level events
205
- # Note: JavaScript execution is not supported - scripts are accessible as text only
208
+ # Note: Scripts are converted from JavaScript to Python and executed in a sandboxed environment
209
+ # during request execution. You can also access script content directly:
206
210
  for request in collection.get_requests():
207
211
  for event in request.events:
208
212
  if event.listen == "prerequest":
209
- print(f"Pre-request script for {request.name}: {event.script}")
213
+ print(f"Pre-request script for {request.name}: {event.get_script_content()}")
210
214
  elif event.listen == "test":
211
- print(f"Test script for {request.name}: {event.script}")
215
+ print(f"Test script for {request.name}: {event.get_script_content()}")
212
216
  ```
213
217
 
214
218
  ### Validation
@@ -259,7 +263,9 @@ async def main():
259
263
  # Create executor
260
264
  executor = RequestExecutor(
261
265
  client_config={"timeout": 30.0, "verify": True},
262
- global_headers={"User-Agent": "python-postman/1.0"}
266
+ global_headers={"User-Agent": "python-postman/1.0"},
267
+ variable_overrides={"env": "production"}, # Highest precedence variables
268
+ request_delay=0.1, # Delay between sequential requests (seconds)
263
269
  )
264
270
 
265
271
  # Create execution context with variables
@@ -322,9 +328,9 @@ async def execute_collection():
322
328
  )
323
329
 
324
330
  # Get the request responses
325
- for result in result.results:
326
- print(f"Request: {result.request.name}")
327
- print(f"Result Text: {result.response.text}")
331
+ for exec_result in result.results:
332
+ print(f"Request: {exec_result.request.name}")
333
+ print(f"Result Text: {exec_result.response.text}")
328
334
 
329
335
  print(f"Parallel execution completed in {result.total_time_ms:.2f}ms")
330
336
 
@@ -457,13 +463,16 @@ from python_postman.execution import (
457
463
  ExecutionError,
458
464
  RequestExecutionError,
459
465
  VariableResolutionError,
460
- AuthenticationError
466
+ AuthenticationError,
467
+ ExecutionTimeoutError,
461
468
  )
462
469
 
463
470
  try:
464
471
  result = await executor.execute_request(request, context)
465
472
  if not result.success:
466
473
  print(f"Request failed: {result.error}")
474
+ except ExecutionTimeoutError as e:
475
+ print(f"Timeout: {e}")
467
476
  except VariableResolutionError as e:
468
477
  print(f"Variable error: {e}")
469
478
  except AuthenticationError as e:
@@ -482,7 +491,7 @@ except RequestExecutionError as e:
482
491
  - **`Folder`**: Container for organizing requests and sub-folders
483
492
  - **`Variable`**: Collection, folder, or request-level variables
484
493
  - **`Auth`**: Authentication configuration
485
- - **`Event`**: Pre-request and test script definitions (text only, execution not supported)
494
+ - **`Event`**: Pre-request and test script definitions (executed in a sandboxed environment during request execution)
486
495
 
487
496
  ### Exception Handling
488
497
 
@@ -508,7 +517,7 @@ except CollectionValidationError as e:
508
517
 
509
518
  ## Requirements
510
519
 
511
- - Python 3.8+
520
+ - Python 3.9+
512
521
  - No external dependencies for core functionality
513
522
 
514
523
  ## Development
@@ -569,15 +578,17 @@ This project is licensed under the MIT License - see the LICENSE file for detail
569
578
 
570
579
  ## Changelog
571
580
 
572
- ### 0.8.0 (Updated version)
573
-
574
- - Updated version to 0.8.0
575
- - Updated README.md
576
- - Updated pyproject.toml
577
- - Updated tests
578
- - Updated docs
579
- - Updated examples
580
- - Updated code
581
+ ### 0.9.0
582
+
583
+ - Added HTTP request execution layer with full async/sync support
584
+ - Added variable resolution with proper scoping and precedence
585
+ - Added authentication handling (Bearer, Basic, API Key)
586
+ - Added request extensions for runtime modification
587
+ - Added search and statistics modules
588
+ - Added introspection utilities (AuthResolver, VariableTracer)
589
+ - Added comprehensive type hints and type safety enhancements
590
+ - Added path parameter support (:parameterName syntax)
591
+ - Added collection and folder execution with parallel mode
581
592
 
582
593
  ## Support
583
594
 
@@ -1,5 +1,7 @@
1
1
  # Python Postman
2
2
 
3
+ > **Disclaimer:** This is an independent, community-maintained open-source project. It is not affiliated with, endorsed by, or sponsored by Postman, Inc. "Postman" is a registered trademark of Postman, Inc.
4
+
3
5
  A comprehensive Python library for working with Postman collections. Parse, execute, search, and analyze Postman collection.json files with a clean, object-oriented interface. Execute HTTP requests with full async/sync support, dynamic variable resolution, and authentication handling.
4
6
 
5
7
  ## Features
@@ -142,11 +144,11 @@ if collection.auth:
142
144
 
143
145
  # Access auth details based on type
144
146
  if collection.auth.type == "bearer":
145
- token = collection.auth.bearer.get("token")
147
+ token = collection.auth.get_bearer_token()
146
148
  print(f"Bearer Token: {token}")
147
149
  elif collection.auth.type == "basic":
148
- username = collection.auth.basic.get("username")
149
- print(f"Basic Auth Username: {username}")
150
+ credentials = collection.auth.get_basic_credentials()
151
+ print(f"Basic Auth Username: {credentials['username']}")
150
152
 
151
153
  # Request-level auth (overrides collection auth)
152
154
  for request in collection.get_requests():
@@ -163,13 +165,14 @@ for event in collection.events:
163
165
  print(f"Script Content: {event.script}")
164
166
 
165
167
  # Access script content from request-level events
166
- # Note: JavaScript execution is not supported - scripts are accessible as text only
168
+ # Note: Scripts are converted from JavaScript to Python and executed in a sandboxed environment
169
+ # during request execution. You can also access script content directly:
167
170
  for request in collection.get_requests():
168
171
  for event in request.events:
169
172
  if event.listen == "prerequest":
170
- print(f"Pre-request script for {request.name}: {event.script}")
173
+ print(f"Pre-request script for {request.name}: {event.get_script_content()}")
171
174
  elif event.listen == "test":
172
- print(f"Test script for {request.name}: {event.script}")
175
+ print(f"Test script for {request.name}: {event.get_script_content()}")
173
176
  ```
174
177
 
175
178
  ### Validation
@@ -220,7 +223,9 @@ async def main():
220
223
  # Create executor
221
224
  executor = RequestExecutor(
222
225
  client_config={"timeout": 30.0, "verify": True},
223
- global_headers={"User-Agent": "python-postman/1.0"}
226
+ global_headers={"User-Agent": "python-postman/1.0"},
227
+ variable_overrides={"env": "production"}, # Highest precedence variables
228
+ request_delay=0.1, # Delay between sequential requests (seconds)
224
229
  )
225
230
 
226
231
  # Create execution context with variables
@@ -283,9 +288,9 @@ async def execute_collection():
283
288
  )
284
289
 
285
290
  # Get the request responses
286
- for result in result.results:
287
- print(f"Request: {result.request.name}")
288
- print(f"Result Text: {result.response.text}")
291
+ for exec_result in result.results:
292
+ print(f"Request: {exec_result.request.name}")
293
+ print(f"Result Text: {exec_result.response.text}")
289
294
 
290
295
  print(f"Parallel execution completed in {result.total_time_ms:.2f}ms")
291
296
 
@@ -418,13 +423,16 @@ from python_postman.execution import (
418
423
  ExecutionError,
419
424
  RequestExecutionError,
420
425
  VariableResolutionError,
421
- AuthenticationError
426
+ AuthenticationError,
427
+ ExecutionTimeoutError,
422
428
  )
423
429
 
424
430
  try:
425
431
  result = await executor.execute_request(request, context)
426
432
  if not result.success:
427
433
  print(f"Request failed: {result.error}")
434
+ except ExecutionTimeoutError as e:
435
+ print(f"Timeout: {e}")
428
436
  except VariableResolutionError as e:
429
437
  print(f"Variable error: {e}")
430
438
  except AuthenticationError as e:
@@ -443,7 +451,7 @@ except RequestExecutionError as e:
443
451
  - **`Folder`**: Container for organizing requests and sub-folders
444
452
  - **`Variable`**: Collection, folder, or request-level variables
445
453
  - **`Auth`**: Authentication configuration
446
- - **`Event`**: Pre-request and test script definitions (text only, execution not supported)
454
+ - **`Event`**: Pre-request and test script definitions (executed in a sandboxed environment during request execution)
447
455
 
448
456
  ### Exception Handling
449
457
 
@@ -469,7 +477,7 @@ except CollectionValidationError as e:
469
477
 
470
478
  ## Requirements
471
479
 
472
- - Python 3.8+
480
+ - Python 3.9+
473
481
  - No external dependencies for core functionality
474
482
 
475
483
  ## Development
@@ -530,15 +538,17 @@ This project is licensed under the MIT License - see the LICENSE file for detail
530
538
 
531
539
  ## Changelog
532
540
 
533
- ### 0.8.0 (Updated version)
534
-
535
- - Updated version to 0.8.0
536
- - Updated README.md
537
- - Updated pyproject.toml
538
- - Updated tests
539
- - Updated docs
540
- - Updated examples
541
- - Updated code
541
+ ### 0.9.0
542
+
543
+ - Added HTTP request execution layer with full async/sync support
544
+ - Added variable resolution with proper scoping and precedence
545
+ - Added authentication handling (Bearer, Basic, API Key)
546
+ - Added request extensions for runtime modification
547
+ - Added search and statistics modules
548
+ - Added introspection utilities (AuthResolver, VariableTracer)
549
+ - Added comprehensive type hints and type safety enhancements
550
+ - Added path parameter support (:parameterName syntax)
551
+ - Added collection and folder execution with parallel mode
542
552
 
543
553
  ## Support
544
554
 
@@ -0,0 +1,147 @@
1
+ # Code Review Audit — python-postman (Revision 4)
2
+
3
+ **Date:** 2026-02-11
4
+ **Scope:** Full codebase review (source, tests, packaging, documentation)
5
+ **Test suite:** 1240 passed, 1 skipped, 2 warnings
6
+
7
+ ---
8
+
9
+ ## Executive Summary
10
+
11
+ python-postman is a well-structured Python library with a clean two-layer architecture (model layer + optional execution layer). The codebase demonstrates solid engineering with comprehensive type hints, thorough validation, and good separation of concerns. The test suite is extensive at 1240 tests, all passing.
12
+
13
+ Across four revision cycles, **all 25 actionable findings from the original audit have been addressed**. The codebase is now in excellent shape. The remaining items are exclusively low-severity cosmetic concerns and inherent architectural limitations that are properly documented.
14
+
15
+ ### Cumulative Fix Tracker
16
+
17
+ | Original Finding | Status |
18
+ |-----------------|--------|
19
+ | #1 exec() security — no sandbox | **Fixed** — restricted builtins + AST validation + thread timeout + docs |
20
+ | #2 Broken test file | **Fixed** — file deleted |
21
+ | #3 TimeoutError shadows builtin | **Fixed** — renamed to ExecutionTimeoutError |
22
+ | #4 AuthType enum duplication | **Fixed** — consolidated to single source of truth (alias) |
23
+ | #5 folder.variable vs folder.variables | **Fixed** — corrected to folder.variables |
24
+ | #6 PostmanResponse.json() bug | **Fixed** — now accesses property correctly |
25
+ | #7 Script timeout ineffective | **Fixed** — thread-based timeout implemented |
26
+ | #8 variable_overrides precedence | **Fixed** — overrides now have higher precedence |
27
+ | #10 Changelog self-contradictory | **Fixed** — rewritten with meaningful entries |
28
+ | #11 async_client leak in close() | **Fixed** — proper cleanup with fallback |
29
+ | #12 Duplicate dev dependency groups | **Fixed** — [dependency-groups] removed |
30
+ | #13 PostmanVariables.has() semantics | **Fixed** — now uses has_variable() |
31
+ | Rev2 #2 request.variable singular form | **Fixed** — changed to request.variables |
32
+ | Rev2 #3 VariableTracer wrong scope | **Fixed** — ENVIRONMENT added to VariableScope enum |
33
+ | Rev2 #5 execute_iter() not true iterator | **Fixed** — new _search_items_iter generator |
34
+ | Rev2 #7 Parent hierarchy not wired | **Fixed** — Collection/Folder.from_dict now wire references |
35
+ | Rev2 #13 troubleshooting.md warnings | **Fixed** — removed non-existent warnings reference |
36
+ | Rev2 #14 optional-dependencies.md [all] | **Fixed** — corrected to show [execution] only |
37
+ | Rev2 #18 TestResult pytest warning | **Fixed** — renamed to CftcTestResult |
38
+ | Rev3 #1 exec() residual risk | **Fixed** — AST validator blocks dunder access; `type` removed from safe builtins; security boundaries documented in docstring |
39
+ | Rev3 #2 AuthType enum duplicated | **Fixed** — `AuthType = AuthTypeEnum` alias, single source of truth |
40
+ | Rev3 #3 execute_collection/folder duplication | **Fixed** — `_execute_requests` extracted |
41
+ | Rev3 #4 Missing __eq__ on ValidationResult | **Fixed** — __eq__ added |
42
+ | Rev3 #8 Collection.to_json type hint | **Fixed** — changed to `Optional[int]` |
43
+ | Rev3 #11 No py.typed marker | **Fixed** — py.typed file added |
44
+
45
+ ---
46
+
47
+ ## Remaining Findings
48
+
49
+ ### LOW
50
+
51
+ #### 1. `to_dict()` Roundtrip Lossy for `Request`
52
+
53
+ **File:** [request.py:185-216](python_postman/models/request.py#L185-L216)
54
+
55
+ `Request.to_dict()` places `description` at the top level of the dict. In actual Postman collection JSON, the `description` field may appear either at the item level or inside the `request` object. The `from_dict` only reads from the top level (`data.get("description")`), which means descriptions nested inside the `request` object are silently dropped during parsing.
56
+
57
+ #### 2. `Request.to_dict()` Omits `protocolProfileBehavior`
58
+
59
+ Postman collections often contain `protocolProfileBehavior` fields on items. The parser silently drops these during `from_dict`, and they're not restored in `to_dict`. This means `from_dict(to_dict())` roundtrips lose data for collections that use this field.
60
+
61
+ #### 3. `Url.__init__` Parameter `hash` Shadows Builtin
62
+
63
+ **File:** [url.py:57](python_postman/models/url.py#L57)
64
+
65
+ The constructor parameter `hash` shadows the Python builtin `hash()` function within the method scope. While not a runtime issue (the parameter name matches the Postman schema field name `hash`), it's a naming concern flagged by linters.
66
+
67
+ ---
68
+
69
+ ### INFORMATIONAL
70
+
71
+ #### 4. JS-to-Python Converter Handles Only Basic Patterns
72
+
73
+ **File:** [script_runner.py:515-588](python_postman/execution/script_runner.py#L515-L588)
74
+
75
+ The `_convert_js_to_python()` method uses ~20 regex substitutions to convert JavaScript to Python. This handles only basic patterns (`var/let/const`, `true/false/null`, `===`, simple `if/else`) and will fail on template literals, destructuring, `for...of`/`for...in` loops, complex arrow functions, class syntax, `try/catch`, and any modern JavaScript features. This limits script execution to only the simplest Postman scripts. This is a known design limitation.
76
+
77
+ #### 5. Large `__init__.py` Re-exports Surface Area
78
+
79
+ **File:** [__init__.py](python_postman/__init__.py)
80
+
81
+ The package re-exports 40+ symbols from its `__init__.py`. While convenient, it means importing the package loads all model classes even if the user only needs one. For a library of this size, the impact is negligible.
82
+
83
+ #### 6. Unawaited Coroutine Warnings in Tests
84
+
85
+ **File:** `tests/test_executor.py`
86
+
87
+ Two tests produce `RuntimeWarning: coroutine 'AsyncMockMixin._execute_mock_call' was never awaited`. This suggests the mock setup for the async executor path may not be fully correct. Harmless but noisy.
88
+
89
+ ---
90
+
91
+ ## Test Suite Summary
92
+
93
+ | Metric | Value |
94
+ |--------|-------|
95
+ | Total tests | 1240 |
96
+ | Passed | 1240 |
97
+ | Skipped | 1 |
98
+ | Errors | 0 |
99
+ | Warnings | 2 (unawaited coroutine warnings in test mocks) |
100
+ | Execution time | 0.50s |
101
+
102
+ **Test coverage areas:**
103
+
104
+ - Model classes: Thorough (all models have dedicated test files)
105
+ - Execution layer: Good (executor, context, variable resolver, auth handler, script runner, extensions)
106
+ - Search/statistics: Covered
107
+ - Introspection: Covered (auth resolver, variable tracer)
108
+ - Edge cases: Dedicated test file
109
+ - Real-world collections: 9 collections tested
110
+ - Integration tests: Present
111
+
112
+ **Gaps:**
113
+
114
+ - No property-based/fuzz testing
115
+ - No benchmarks for large collection parsing performance
116
+
117
+ ---
118
+
119
+ ## Architecture Assessment
120
+
121
+ **Strengths:**
122
+
123
+ - Clean separation between parsing (zero dependencies) and execution (optional httpx)
124
+ - Consistent `from_dict()` / `to_dict()` pattern across all models
125
+ - Fluent search API with true iterator support (`execute_iter()` uses a generator)
126
+ - Comprehensive exception hierarchy with contextual details
127
+ - Well-implemented variable scoping with correct precedence rules
128
+ - Good use of ABC for Item base class with factory methods
129
+ - Layered script sandbox: restricted builtins + import blocklist + AST validation + thread timeout, with security boundaries clearly documented
130
+ - Proper async client cleanup in `close()` with sync/async fallback
131
+ - Parent hierarchy (`_parent_folder`, `_collection`) properly wired during parsing
132
+ - `VariableScope` enum includes all four scopes (REQUEST, FOLDER, COLLECTION, ENVIRONMENT)
133
+ - Single source of truth for `AuthType` via alias to `AuthTypeEnum`
134
+ - DRY execution logic via shared `_execute_requests` method
135
+ - PEP 561 compliant with `py.typed` marker
136
+
137
+ **No significant weaknesses remain.** The only architectural limitations are inherent to the design (regex-based JS conversion, `exec()` for script execution) and are properly documented.
138
+
139
+ ---
140
+
141
+ ## Recommendations Priority
142
+
143
+ | Priority | Finding | Effort |
144
+ |----------|---------|--------|
145
+ | P3 | Handle `description` inside `request` object during parsing (#1) | Low |
146
+ | P3 | Preserve `protocolProfileBehavior` in roundtrips (#2) | Low |
147
+ | P4 | No action needed — remaining items are cosmetic/informational | — |
@@ -56,8 +56,7 @@ pip install python-postman[execution]
56
56
  from python_postman import PythonPostman
57
57
 
58
58
  # Parse collection
59
- parser = PythonPostman()
60
- collection = parser.parse("collection.json")
59
+ collection = PythonPostman.from_file("collection.json")
61
60
 
62
61
  # Analyze
63
62
  print(f"Collection: {collection.info.name}")
@@ -76,8 +75,7 @@ from python_postman import PythonPostman
76
75
  from python_postman.execution import RequestExecutor, ExecutionContext
77
76
 
78
77
  # Parse collection
79
- parser = PythonPostman()
80
- collection = parser.parse("collection.json")
78
+ collection = PythonPostman.from_file("collection.json")
81
79
 
82
80
  # Setup execution
83
81
  executor = RequestExecutor()
@@ -85,10 +83,10 @@ context = ExecutionContext()
85
83
  context.set_variable("api_key", "your-key")
86
84
 
87
85
  # Execute
88
- results = await executor.execute_collection(collection, context=context)
86
+ collection_result = await executor.execute_collection(collection, context=context)
89
87
 
90
88
  # Check results
91
- for result in results:
89
+ for result in collection_result.results:
92
90
  print(f"{result.request.name}: {result.response.status_code}")
93
91
  ```
94
92
 
@@ -143,7 +141,7 @@ for result in results:
143
141
  **Example:**
144
142
 
145
143
  ```python
146
- collection = parser.parse("collection.json")
144
+ collection = PythonPostman.from_file("collection.json")
147
145
 
148
146
  # Validate structure
149
147
  result = collection.validate()
@@ -177,7 +175,7 @@ for search_result in critical:
177
175
  **Example:**
178
176
 
179
177
  ```python
180
- collection = parser.parse("staging_collection.json")
178
+ collection = PythonPostman.from_file("staging_collection.json")
181
179
 
182
180
  # Update URLs
183
181
  for request in collection.get_requests():
@@ -207,7 +205,7 @@ with open("production_collection.json", "w") as f:
207
205
  ```python
208
206
  from python_postman.introspection import AuthResolver
209
207
 
210
- collection = parser.parse("collection.json")
208
+ collection = PythonPostman.from_file("collection.json")
211
209
 
212
210
  # Analyze authentication
213
211
  for request in collection.get_requests():
@@ -294,12 +292,12 @@ Collection
294
292
 
295
293
  Variables can be defined at multiple levels:
296
294
 
297
- 1. Collection variables
298
- 2. Environment variables
299
- 3. Global variables
300
- 4. Request variables (set in scripts)
295
+ 1. Request variables (highest precedence)
296
+ 2. Folder variables
297
+ 3. Collection variables
298
+ 4. Environment variables (lowest precedence)
301
299
 
302
- **Precedence:** Request > Environment > Collection > Global
300
+ **Precedence:** Request > Folder > Collection > Environment
303
301
 
304
302
  ### Authentication
305
303