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.
- {python_postman-0.9.0 → python_postman-0.9.1}/PKG-INFO +41 -30
- {python_postman-0.9.0 → python_postman-0.9.1}/README.md +32 -22
- python_postman-0.9.1/audit.md +147 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/README.md +12 -14
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/architecture/execution-layer.md +74 -99
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/architecture/layer-interaction.md +20 -29
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/architecture/model-layer.md +8 -11
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/architecture/overview.md +4 -3
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/examples/generate-documentation.py +1 -2
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/examples/parse-inspect-modify-execute.py +27 -33
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/guides/decision-tree.md +13 -19
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/guides/description-fields.md +6 -8
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/guides/optional-dependencies.md +7 -14
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/guides/troubleshooting.md +17 -20
- python_postman-0.9.1/docs/usage.md +1772 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/advanced_execution.py +1 -1
- python_postman-0.9.1/examples/eia_api_example.py +231 -0
- python_postman-0.9.1/plan.md +681 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/pyproject.toml +13 -16
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/__init__.py +3 -3
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/__init__.py +2 -2
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/exceptions.py +1 -1
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/executor.py +90 -126
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/script_runner.py +151 -7
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/variable_resolver.py +1 -1
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/introspection/variable_tracer.py +1 -1
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/auth.py +2 -20
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/collection.py +52 -1
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/folder.py +10 -1
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/variable.py +1 -0
- python_postman-0.9.1/python_postman/py.typed +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/search/query.py +34 -6
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/statistics/collector.py +1 -2
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/types/auth_types.py +4 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/test_cftc_comprehensive.py +8 -8
- python_postman-0.9.1/tests/test_data/real_world/EIA_APIv2.postman_collection.json +37474 -0
- python_postman-0.9.1/tests/test_data/real_world/Genscape_Oil_Storage.postman_collection.json +1047 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_execution_exceptions.py +13 -13
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_executor.py +5 -5
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_imports.py +4 -4
- python_postman-0.9.1/tests/test_real_world_collections.py +158 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_script_runner.py +2 -3
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_type_safety.py +1 -1
- {python_postman-0.9.0 → python_postman-0.9.1}/uv.lock +20 -13
- python_postman-0.9.0/tests/test_execution_reporter.py +0 -268
- {python_postman-0.9.0 → python_postman-0.9.1}/.github/workflows/python-publish.yml +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/.gitignore +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/LICENSE +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/coverage.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/examples/description-usage.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/examples/variable-introspection.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/docs/guides/variable-scoping.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/README.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/auth_resolution_example.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/authentication_examples.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/basic_execution.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/cftc_execution_example.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/complete_workflow.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/multi_collection_execution.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/path_parameters_example.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/README.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/README.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/collections/auth-service.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/collections/order-service.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/collections/user-service.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/environments/microservices.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/microservices-testing/main.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/README.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/collections/rest-api.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/environments/dev.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/environments/prod.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/main.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/requirements.txt +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/projects/rest-api-testing/tests/test_rest_api.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/request_convenience_methods.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/search_example.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/statistics_example.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/synchronous_execution.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/usage_patterns.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/examples/variable_examples.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/__init__.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/base.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/file_error.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/parse_error.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/schema_error.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/exceptions/validation_error.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/auth_handler.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/context.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/extensions.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/response.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/execution/results.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/introspection/__init__.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/introspection/auth_resolver.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/__init__.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/body.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/collection_info.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/cookie.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/event.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/header.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/item.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/request.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/response.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/schema.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/models/url.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/parser.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/search/__init__.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/statistics/__init__.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/types/__init__.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/types/http_methods.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/utils/__init__.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/utils/json_parser.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/python_postman/utils/validators.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/test_execution_summary.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/__init__.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/CFTC_TEST_SUMMARY.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/INDEX.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/QUICK_START_CFTC.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/README_CFTC_TESTS.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/execution_tests/example_cftc_usage.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_auth.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_auth_handler.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_auth_resolver.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_body.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_collection.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_collection_info.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_cookie.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/README.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/README_integration_tests.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/auth_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/auth_execution_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/cftc.gov.postman_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/empty_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/error_scenarios_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/events_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/execution_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/invalid_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/malformed_json.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/nested_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/nested_execution_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/performance_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_data/simple_collection.json +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_description_fields.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_edge_cases.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_edge_cases.py.bak +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_edge_cases_summary.md +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_event.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_exceptions.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_execution_context.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_extensions.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_folder.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_header.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_integration.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_item.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_item_clarity.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_json_parser.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_parser.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_request.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_request_convenience.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_request_execution.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_request_responses.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_response.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_response_cookies.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_schema.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_search.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_statistics.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_url.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_variable.py +0 -0
- {python_postman-0.9.0 → python_postman-0.9.1}/tests/test_variable_resolver.py +0 -0
- {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.
|
|
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/
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
7
|
-
Project-URL: Documentation, https://python-postman.
|
|
8
|
-
Project-URL: Bug Tracker, https://github.com/
|
|
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
|
-
|
|
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:
|
|
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.
|
|
187
|
+
token = collection.auth.get_bearer_token()
|
|
185
188
|
print(f"Bearer Token: {token}")
|
|
186
189
|
elif collection.auth.type == "basic":
|
|
187
|
-
|
|
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:
|
|
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.
|
|
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.
|
|
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
|
|
326
|
-
print(f"Request: {
|
|
327
|
-
print(f"Result 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 (
|
|
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.
|
|
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.
|
|
573
|
-
|
|
574
|
-
-
|
|
575
|
-
-
|
|
576
|
-
-
|
|
577
|
-
-
|
|
578
|
-
-
|
|
579
|
-
-
|
|
580
|
-
-
|
|
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.
|
|
147
|
+
token = collection.auth.get_bearer_token()
|
|
146
148
|
print(f"Bearer Token: {token}")
|
|
147
149
|
elif collection.auth.type == "basic":
|
|
148
|
-
|
|
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:
|
|
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.
|
|
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.
|
|
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
|
|
287
|
-
print(f"Request: {
|
|
288
|
-
print(f"Result 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 (
|
|
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.
|
|
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.
|
|
534
|
-
|
|
535
|
-
-
|
|
536
|
-
-
|
|
537
|
-
-
|
|
538
|
-
-
|
|
539
|
-
-
|
|
540
|
-
-
|
|
541
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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.
|
|
298
|
-
2.
|
|
299
|
-
3.
|
|
300
|
-
4.
|
|
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 >
|
|
300
|
+
**Precedence:** Request > Folder > Collection > Environment
|
|
303
301
|
|
|
304
302
|
### Authentication
|
|
305
303
|
|