clear-skies 2.0.10__tar.gz → 2.0.12__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of clear-skies might be problematic. Click here for more details.
- {clear_skies-2.0.10 → clear_skies-2.0.12}/CHANGELOG.md +16 -0
- clear_skies-2.0.12/LATEST_CHANGELOG.md +6 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/PKG-INFO +1 -1
- {clear_skies-2.0.10 → clear_skies-2.0.12}/pyproject.toml +1 -1
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/audit.py +3 -3
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/__init__.py +2 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/endpoint_list.py +1 -1
- clear_skies-2.0.12/src/clearskies/configs/headers.py +30 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/contexts/cli.py +5 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/contexts/context.py +4 -1
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/contexts/wsgi.py +5 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/di.py +11 -5
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/end.py +0 -2
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoint.py +2 -2
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/input_outputs/cli.py +15 -31
- clear_skies-2.0.12/src/clearskies/input_outputs/headers.py +54 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/input_outputs/input_output.py +20 -43
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/input_outputs/programmatic.py +13 -20
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/input_outputs/wsgi.py +18 -16
- {clear_skies-2.0.10 → clear_skies-2.0.12}/uv.lock +1 -1
- clear_skies-2.0.10/LATEST_CHANGELOG.md +0 -7
- clear_skies-2.0.10/src/clearskies/input_outputs/headers.py +0 -47
- {clear_skies-2.0.10 → clear_skies-2.0.12}/.editorconfig +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/.github/workflows/create-version.yaml +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/.github/workflows/docs.yaml +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/.github/workflows/run-tests.yml +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/.github/workflows/tests-matrix.yaml +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/.github/workflows/tests.yaml +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/.gitignore +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/.pre-commit-config.yaml +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/.python-version +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/.vscode/extensions.json +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/.vscode/settings.json +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/LICENSE +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/README.md +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/build.sh +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/cliff.toml +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/ruff.toml +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/action.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/authentication/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/authentication/authentication.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/authentication/authorization.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/authentication/authorization_pass_through.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/authentication/jwks.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/authentication/public.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/authentication/secret_bearer.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/oai3_json.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/oai3_schema_resolver.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/parameter.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/request.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/response.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/schema/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/schema/array.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/schema/default.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/schema/enum.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/schema/object.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/formats/oai3_json/test.json +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/py.typed +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/request/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/request/header.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/request/json_body.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/request/parameter.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/request/request.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/request/url_parameter.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/request/url_path.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/response/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/response/response.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/array.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/base64.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/boolean.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/date.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/datetime.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/double.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/enum.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/integer.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/long.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/number.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/object.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/password.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/schema.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/autodoc/schema/string.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/backends/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/backends/api_backend.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/backends/backend.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/backends/cursor_backend.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/backends/memory_backend.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/backends/secrets_backend.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/column.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/belongs_to_id.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/belongs_to_model.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/belongs_to_self.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/boolean.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/category_tree.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/category_tree_ancestors.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/category_tree_children.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/category_tree_descendants.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/created.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/created_by_authorization_data.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/created_by_header.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/created_by_ip.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/created_by_routing_data.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/created_by_user_agent.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/date.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/datetime.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/email.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/float.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/has_many.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/has_many_self.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/has_one.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/integer.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/json.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/many_to_many_ids.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/many_to_many_ids_with_data.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/many_to_many_models.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/many_to_many_pivots.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/phone.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/select.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/string.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/timestamp.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/updated.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/columns/uuid.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/README.md +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/actions.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/any.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/any_dict.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/any_dict_or_callable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/authentication.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/authorization.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/boolean.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/boolean_or_callable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/callable_config.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/columns.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/conditions.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/config.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/datetime.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/datetime_or_callable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/email.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/email_list.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/email_list_or_callable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/email_or_email_list_or_callable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/endpoint.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/float.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/float_or_callable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/integer.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/integer_or_callable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/joins.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/list_any_dict.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/list_any_dict_or_callable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/model_class.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/model_column.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/model_columns.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/model_destination_name.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/model_to_id_column.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/readable_model_column.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/readable_model_columns.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/schema.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/searchable_model_columns.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/security_headers.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/select.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/select_list.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/string.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/string_dict.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/string_list.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/string_list_or_callable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/string_or_callable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/timedelta.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/timezone.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/url.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/validators.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/writeable_model_column.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configs/writeable_model_columns.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/configurable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/contexts/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/contexts/wsgi_ref.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/decorators.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/decorators.pyi +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/additional_config.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/additional_config_auto_import.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/inject/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/inject/by_class.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/inject/by_name.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/inject/di.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/inject/environment.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/inject/input_output.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/inject/now.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/inject/requests.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/inject/secrets.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/inject/utcnow.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/inject/uuid.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/injectable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/injectable_properties.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/test_module/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/test_module/another_module/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/di/test_module/module_class.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoint_group.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/advanced_search.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/callable.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/create.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/delete.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/get.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/health_check.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/list.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/restful_api.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/schema.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/simple_search.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/endpoints/update.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/environment.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/exceptions/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/exceptions/authentication.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/exceptions/authorization.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/exceptions/client_error.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/exceptions/input_errors.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/exceptions/missing_dependency.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/exceptions/moved_permanently.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/exceptions/moved_temporarily.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/exceptions/not_found.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/functional/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/functional/routing.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/functional/string.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/functional/validations.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/input_outputs/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/input_outputs/exceptions/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/input_outputs/exceptions/cli_input_error.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/input_outputs/exceptions/cli_not_found.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/input_outputs/py.typed +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/model.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/py.typed +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/query/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/query/condition.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/query/join.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/query/query.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/query/sort.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/schema.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/secrets/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/secrets/additional_configs/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/secrets/additional_configs/mysql_connection_dynamic_producer.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/secrets/akeyless.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/secrets/exceptions/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/secrets/exceptions/not_found.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/secrets/secrets.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/security_header.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/security_headers/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/security_headers/cache_control.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/security_headers/cors.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/security_headers/csp.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/security_headers/hsts.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/security_headers/x_content_type_options.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/security_headers/x_frame_options.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/test_base.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/typing.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validator.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/__init__.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/after_column.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/before_column.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/in_the_future.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/in_the_future_at_least.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/in_the_future_at_most.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/in_the_past.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/in_the_past_at_least.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/in_the_past_at_most.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/maximum_length.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/maximum_value.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/minimum_length.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/minimum_value.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/required.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/timedelta.py +0 -0
- {clear_skies-2.0.10 → clear_skies-2.0.12}/src/clearskies/validators/unique.py +0 -0
|
@@ -5,9 +5,23 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.12] - 2025-10-09
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- Use dict for Header class by @cmancone in [#23](https://github.com/clearskies-py/clearskies/pull/23)
|
|
12
|
+
- Make headers a dict
|
|
13
|
+
|
|
14
|
+
## [2.0.11] - 2025-10-09
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Bump version to v2.0.11 by @github-actions[bot]
|
|
18
|
+
- Typos by @cmancone in [#24](https://github.com/clearskies-py/clearskies/pull/24)
|
|
19
|
+
- Input Output reorg by @cmancone
|
|
20
|
+
|
|
8
21
|
## [2.0.10] - 2025-10-06
|
|
9
22
|
|
|
10
23
|
### Changed
|
|
24
|
+
- Bump version to v2.0.10 by @github-actions[bot]
|
|
11
25
|
- Include injectable props for base secret class, and detection of… by @cmancone in [#22](https://github.com/clearskies-py/clearskies/pull/22)
|
|
12
26
|
- Black by @cmancone
|
|
13
27
|
- Include injectable props for base secret class, and detection of missing class by @cmancone
|
|
@@ -799,6 +813,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
799
813
|
* @ made their first contribution
|
|
800
814
|
* @tnijboer made their first contribution
|
|
801
815
|
* @conormancone-cimpress made their first contribution
|
|
816
|
+
[2.0.12]: https://github.com/clearskies-py/clearskies/compare/v2.0.11..v2.0.12
|
|
817
|
+
[2.0.11]: https://github.com/clearskies-py/clearskies/compare/v2.0.10..v2.0.11
|
|
802
818
|
[2.0.10]: https://github.com/clearskies-py/clearskies/compare/v2.0.9..v2.0.10
|
|
803
819
|
[2.0.9]: https://github.com/clearskies-py/clearskies/compare/v2.0.8..v2.0.9
|
|
804
820
|
[2.0.8]: https://github.com/clearskies-py/clearskies/compare/v2.0.7..v2.0.8
|
|
@@ -155,7 +155,7 @@ class Audit(HasMany):
|
|
|
155
155
|
},
|
|
156
156
|
)
|
|
157
157
|
|
|
158
|
-
def post_delete(self, model):
|
|
158
|
+
def post_delete(self, model: Model) -> None:
|
|
159
159
|
super().post_delete(model)
|
|
160
160
|
exclude_columns = self.exclude_columns
|
|
161
161
|
model_columns = self.get_model_columns()
|
|
@@ -167,7 +167,7 @@ class Audit(HasMany):
|
|
|
167
167
|
continue
|
|
168
168
|
final_data = {
|
|
169
169
|
**final_data,
|
|
170
|
-
**(model_columns[key].to_json(model) if key in model_columns else {key: model.
|
|
170
|
+
**(model_columns[key].to_json(model) if key in model_columns else {key: model.get_raw_data().get(key)}),
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
for key in mask_columns:
|
|
@@ -178,7 +178,7 @@ class Audit(HasMany):
|
|
|
178
178
|
self.child_model.create(
|
|
179
179
|
{
|
|
180
180
|
"class": self.model_class.__name__,
|
|
181
|
-
"resource_id": model
|
|
181
|
+
"resource_id": getattr(model, self.model_class.id_column_name),
|
|
182
182
|
"action": "delete",
|
|
183
183
|
"data": final_data,
|
|
184
184
|
}
|
|
@@ -87,6 +87,7 @@ from .endpoint import Endpoint
|
|
|
87
87
|
from .endpoint_list import EndpointList
|
|
88
88
|
from .float import Float
|
|
89
89
|
from .float_or_callable import FloatOrCallable
|
|
90
|
+
from .headers import Headers
|
|
90
91
|
from .integer import Integer
|
|
91
92
|
from .integer_or_callable import IntegerOrCallable
|
|
92
93
|
from .joins import Joins
|
|
@@ -139,6 +140,7 @@ __all__ = [
|
|
|
139
140
|
"EndpointList",
|
|
140
141
|
"Float",
|
|
141
142
|
"FloatOrCallable",
|
|
143
|
+
"Headers",
|
|
142
144
|
"Joins",
|
|
143
145
|
"Integer",
|
|
144
146
|
"IntegerOrCallable",
|
|
@@ -19,7 +19,7 @@ class EndpointList(config.Config):
|
|
|
19
19
|
if not hasattr(item, "top_level_authentication_and_authorization"):
|
|
20
20
|
error_prefix = self._error_prefix(instance)
|
|
21
21
|
raise TypeError(
|
|
22
|
-
f"{error_prefix} attempt to set a value of type '{item.__class__.__name__}' for item #{index+1} when all items in the list should be instances of clearskies.End."
|
|
22
|
+
f"{error_prefix} attempt to set a value of type '{item.__class__.__name__}' for item #{index + 1} when all items in the list should be instances of clearskies.End."
|
|
23
23
|
)
|
|
24
24
|
instance._set_config(self, value)
|
|
25
25
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from clearskies.configs import config
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from clearskies.input_outputs import headers
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Headers(config.Config):
|
|
12
|
+
"""
|
|
13
|
+
This is for a configuration that should be an instance of type clearskies.input_outputs.Headers.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __set__(self, instance, value: headers.Headers):
|
|
17
|
+
if value is None:
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
if not hasattr(value, "_duck_cheat") or value._duck_cheat != "headers":
|
|
21
|
+
error_prefix = self._error_prefix(instance)
|
|
22
|
+
raise TypeError(
|
|
23
|
+
f"{error_prefix} attempt to set a value of type '{value.__class__.__name__}' to a property that expets an instance of clearskies.input_outputs.Headers"
|
|
24
|
+
)
|
|
25
|
+
instance._set_config(self, value)
|
|
26
|
+
|
|
27
|
+
def __get__(self, instance, parent) -> headers.Headers:
|
|
28
|
+
if not instance:
|
|
29
|
+
return self # type: ignore
|
|
30
|
+
return instance._get_config(self)
|
|
@@ -119,6 +119,11 @@ class Cli(Context):
|
|
|
119
119
|
Although note that the first two are going to be preferred over the third, simply because with the
|
|
120
120
|
third there's simply no way to specify the type of a variable. As a result, you may run into issues
|
|
121
121
|
with strict type checking on endpoints.
|
|
122
|
+
|
|
123
|
+
### Context Callables
|
|
124
|
+
|
|
125
|
+
When using the Cli context, an additional named argument is made available to any callables invoked by clearskies:
|
|
126
|
+
`sys_argv`. This contains `sys.argv`.
|
|
122
127
|
"""
|
|
123
128
|
|
|
124
129
|
def __call__(self): # type: ignore
|
|
@@ -7,12 +7,12 @@ from typing import TYPE_CHECKING, Any, Callable
|
|
|
7
7
|
from clearskies import exceptions
|
|
8
8
|
from clearskies.di import Di
|
|
9
9
|
from clearskies.di.additional_config import AdditionalConfig
|
|
10
|
+
from clearskies.input_outputs import InputOutput
|
|
10
11
|
from clearskies.input_outputs import Programmatic
|
|
11
12
|
|
|
12
13
|
if TYPE_CHECKING:
|
|
13
14
|
from clearskies.endpoint import Endpoint
|
|
14
15
|
from clearskies.endpoint_group import EndpointGroup
|
|
15
|
-
from clearskies.input_outputs import InputOutput
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class Context:
|
|
@@ -54,6 +54,9 @@ class Context:
|
|
|
54
54
|
self.application = application
|
|
55
55
|
|
|
56
56
|
def execute_application(self, input_output: InputOutput):
|
|
57
|
+
self.di.add_binding("input_output", input_output)
|
|
58
|
+
self.di.add_class_override(InputOutput, input_output)
|
|
59
|
+
|
|
57
60
|
if hasattr(self.application, "injectable_properties"):
|
|
58
61
|
self.application.injectable_properties(self.di)
|
|
59
62
|
return self.application(input_output)
|
|
@@ -72,6 +72,11 @@ class Wsgi(Context):
|
|
|
72
72
|
by and large it's normal and expected that you'll persist the cache between requests by creating the context outside
|
|
73
73
|
of any handler functions.
|
|
74
74
|
|
|
75
|
+
### Context for Callables
|
|
76
|
+
|
|
77
|
+
When using this context, one additional named property becomes available to any callables invoked by clearskies:
|
|
78
|
+
`wsgi_environment`. This contains the environment object passed in by the WSGI server to clearskies.
|
|
79
|
+
|
|
75
80
|
"""
|
|
76
81
|
|
|
77
82
|
def __call__(self, env, start_response): # type: ignore
|
|
@@ -520,7 +520,7 @@ class Di:
|
|
|
520
520
|
"""
|
|
521
521
|
if not inspect.isclass(class_to_override):
|
|
522
522
|
raise ValueError(
|
|
523
|
-
"Invalid value passed to add_class_override for '
|
|
523
|
+
"Invalid value passed to add_class_override for 'class_to_override' parameter: it was not a class."
|
|
524
524
|
)
|
|
525
525
|
|
|
526
526
|
self._class_overrides_by_class[class_to_override] = replacement
|
|
@@ -723,12 +723,18 @@ class Di:
|
|
|
723
723
|
return None
|
|
724
724
|
if not callable(class_to_build):
|
|
725
725
|
return None
|
|
726
|
-
if inspect.isabstract(class_to_build):
|
|
727
|
-
return None
|
|
728
726
|
|
|
729
|
-
#
|
|
727
|
+
# check our class overrides
|
|
730
728
|
if class_to_build in self._class_overrides_by_class:
|
|
731
|
-
|
|
729
|
+
replacement = self._class_overrides_by_class[class_to_build]
|
|
730
|
+
if not inspect.isclass(replacement):
|
|
731
|
+
return replacement
|
|
732
|
+
return self.build_class(replacement, context=context, cache=cache)
|
|
733
|
+
|
|
734
|
+
# generally we can't build abstract classes, so if the class is abstract then we should pass.
|
|
735
|
+
# However, this is not the case if it has an override - then the developer has given us specific guidance
|
|
736
|
+
if inspect.isabstract(class_to_build):
|
|
737
|
+
return None
|
|
732
738
|
|
|
733
739
|
# next check our additional config classes
|
|
734
740
|
built_value = None
|
|
@@ -88,8 +88,6 @@ class End(
|
|
|
88
88
|
if response:
|
|
89
89
|
return response
|
|
90
90
|
|
|
91
|
-
self.di.add_binding("input_output", input_output)
|
|
92
|
-
|
|
93
91
|
# catch everything when we do an AuthN/AuthZ check because we allow custom-defined classes,
|
|
94
92
|
# and this gives more flexibility (or possibly forgiveness) for how they raise exceptions.
|
|
95
93
|
try:
|
|
@@ -969,7 +969,7 @@ class Endpoint(
|
|
|
969
969
|
"""Whether or not we can handle an incoming request based on URL and request method."""
|
|
970
970
|
# soo..... this excessively duplicates the logic in __call__, but I'm being lazy right now
|
|
971
971
|
# and not fixing it.
|
|
972
|
-
request_method = input_output.
|
|
972
|
+
request_method = input_output.request_method.upper()
|
|
973
973
|
if request_method == "OPTIONS":
|
|
974
974
|
return True
|
|
975
975
|
if request_method not in self.request_methods:
|
|
@@ -986,7 +986,7 @@ class Endpoint(
|
|
|
986
986
|
# matches_request is only checked by the endpoint group, not by the context. As a result, we need to check our
|
|
987
987
|
# route. However we always have to check our route anyway because the full routing data can only be figured
|
|
988
988
|
# out at the endpoint level, so calling out to routing.mattch_route is unavoidable.
|
|
989
|
-
request_method = input_output.
|
|
989
|
+
request_method = input_output.request_method.upper()
|
|
990
990
|
if request_method == "OPTIONS":
|
|
991
991
|
return self.cors(input_output)
|
|
992
992
|
if request_method not in self.request_methods:
|
|
@@ -5,19 +5,16 @@ import sys
|
|
|
5
5
|
from os import isatty
|
|
6
6
|
from sys import stdin
|
|
7
7
|
|
|
8
|
+
from clearskies.input_outputs.headers import Headers
|
|
8
9
|
from clearskies.input_outputs.input_output import InputOutput
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class Cli(InputOutput):
|
|
12
|
-
|
|
13
|
+
path: str
|
|
13
14
|
_has_body: bool = False
|
|
14
15
|
_body: str = ""
|
|
15
|
-
_request_method: str = ""
|
|
16
|
-
_request_headers: dict[str, str] = {}
|
|
17
16
|
|
|
18
17
|
def __init__(self):
|
|
19
|
-
self._request_headers = {}
|
|
20
|
-
self._args = []
|
|
21
18
|
self._parse_args(sys.argv)
|
|
22
19
|
super().__init__()
|
|
23
20
|
|
|
@@ -30,16 +27,13 @@ class Cli(InputOutput):
|
|
|
30
27
|
sys.exit(final)
|
|
31
28
|
print(final)
|
|
32
29
|
|
|
33
|
-
def get_arguments(self):
|
|
34
|
-
return sys.argv
|
|
35
|
-
|
|
36
30
|
def _parse_args(self, argv):
|
|
37
31
|
tty_data = None
|
|
38
32
|
if not isatty(stdin.fileno()):
|
|
39
33
|
tty_data = sys.stdin.read().strip()
|
|
40
34
|
|
|
41
35
|
request_headers = {}
|
|
42
|
-
|
|
36
|
+
args = []
|
|
43
37
|
kwargs = {}
|
|
44
38
|
index = 0
|
|
45
39
|
# In general we will use positional arguments for routing, and kwargs for request data.
|
|
@@ -49,10 +43,10 @@ class Cli(InputOutput):
|
|
|
49
43
|
while index < len(argv) - 1:
|
|
50
44
|
index += 1
|
|
51
45
|
|
|
52
|
-
# if we don't start with a dash then we are a positional argument
|
|
46
|
+
# if we don't start with a dash then we are a positional argument which are used for building the URL-equivalent
|
|
53
47
|
arg = argv[index]
|
|
54
48
|
if arg[0] != "-":
|
|
55
|
-
|
|
49
|
+
args.append(arg)
|
|
56
50
|
continue
|
|
57
51
|
|
|
58
52
|
# otherwise a kwarg
|
|
@@ -83,8 +77,8 @@ class Cli(InputOutput):
|
|
|
83
77
|
|
|
84
78
|
kwargs[key] = value
|
|
85
79
|
|
|
86
|
-
self.
|
|
87
|
-
self.
|
|
80
|
+
self.request_headers = Headers(request_headers)
|
|
81
|
+
self.request_method = "GET"
|
|
88
82
|
request_method_source = ""
|
|
89
83
|
for key in ["x", "X", "request_method"]:
|
|
90
84
|
if key not in kwargs:
|
|
@@ -94,7 +88,7 @@ class Cli(InputOutput):
|
|
|
94
88
|
raise ValueError(
|
|
95
89
|
f"Invalid clearskies cli calling sequence: the request method was specified via both the -{key} parameter and the -{request_method_source} parameter. To avoid ambiguity, it should only be set once."
|
|
96
90
|
)
|
|
97
|
-
self.
|
|
91
|
+
self.request_method = kwargs[key].upper()
|
|
98
92
|
del kwargs[key]
|
|
99
93
|
request_method_source = key
|
|
100
94
|
|
|
@@ -127,6 +121,8 @@ class Cli(InputOutput):
|
|
|
127
121
|
final_data = kwargs
|
|
128
122
|
data_source = "kwargs"
|
|
129
123
|
|
|
124
|
+
self.path = "/".join(args)
|
|
125
|
+
|
|
130
126
|
# Most of the above inputs result in a string for our final data, in which case we'll leave it as the "raw body"
|
|
131
127
|
# so that it can optionally be interpreted as JSON. If we received a bunch of kwargs though, we'll allow those to
|
|
132
128
|
# only be "read" as JSON.
|
|
@@ -139,17 +135,8 @@ class Cli(InputOutput):
|
|
|
139
135
|
self._has_body = True
|
|
140
136
|
self._body = final_data
|
|
141
137
|
|
|
142
|
-
def get_script_name(self):
|
|
143
|
-
return sys.argv[0]
|
|
144
|
-
|
|
145
|
-
def get_path_info(self):
|
|
146
|
-
return "/".join(self._args)
|
|
147
|
-
|
|
148
138
|
def get_full_path(self):
|
|
149
|
-
return self.
|
|
150
|
-
|
|
151
|
-
def get_request_method(self):
|
|
152
|
-
return self._request_method
|
|
139
|
+
return self.path
|
|
153
140
|
|
|
154
141
|
def has_body(self):
|
|
155
142
|
return self._has_body
|
|
@@ -160,14 +147,11 @@ class Cli(InputOutput):
|
|
|
160
147
|
|
|
161
148
|
return self._body
|
|
162
149
|
|
|
150
|
+
def get_protocol(self):
|
|
151
|
+
return "cli"
|
|
152
|
+
|
|
163
153
|
def context_specifics(self):
|
|
164
|
-
return {}
|
|
154
|
+
return {"sys_argv": sys.argv}
|
|
165
155
|
|
|
166
156
|
def get_client_ip(self):
|
|
167
157
|
return "127.0.0.1"
|
|
168
|
-
|
|
169
|
-
def get_query_string(self):
|
|
170
|
-
return ""
|
|
171
|
-
|
|
172
|
-
def get_request_headers(self):
|
|
173
|
-
return self._request_headers
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import TypeVar
|
|
5
|
+
|
|
6
|
+
_T = TypeVar("_T")
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Headers(dict[str, str]):
|
|
10
|
+
_duck_cheat = "headers"
|
|
11
|
+
|
|
12
|
+
def __init__(self, headers: dict[str, str] = {}) -> None:
|
|
13
|
+
normalized_headers = (
|
|
14
|
+
{key.upper().replace("_", "-"): value for (key, value) in headers.items()} if headers else {}
|
|
15
|
+
)
|
|
16
|
+
super().__init__(normalized_headers)
|
|
17
|
+
|
|
18
|
+
def __contains__(self, key: object) -> bool:
|
|
19
|
+
if not isinstance(key, str):
|
|
20
|
+
return False
|
|
21
|
+
return super().__contains__(key.upper().replace("_", "-"))
|
|
22
|
+
|
|
23
|
+
def __getitem__(self, key: str) -> str:
|
|
24
|
+
return super().__getitem__(key.upper().replace("_", "-"))
|
|
25
|
+
|
|
26
|
+
def __setitem__(self, key: str, value: str) -> None:
|
|
27
|
+
if not isinstance(key, str):
|
|
28
|
+
raise TypeError(
|
|
29
|
+
f"Header keys must be strings, but an object of type '{key.__class__.__name__}' was provided."
|
|
30
|
+
)
|
|
31
|
+
if not isinstance(value, str):
|
|
32
|
+
raise TypeError(
|
|
33
|
+
f"Header values must be strings, but an object of type '{value.__class__.__name__}' was provided."
|
|
34
|
+
)
|
|
35
|
+
normalized_key = re.sub("\\s+", " ", key.upper().replace("_", "-"))
|
|
36
|
+
normalized_value = re.sub("\\s+", " ", value.strip())
|
|
37
|
+
super().__setitem__(normalized_key, normalized_value)
|
|
38
|
+
|
|
39
|
+
def __getattr__(self, key: str) -> str | None:
|
|
40
|
+
return self.get(key.upper().replace("_", "-"), None)
|
|
41
|
+
|
|
42
|
+
def __setattr__(self, key: str, value: str) -> None:
|
|
43
|
+
if key.startswith("_") or key == "_duck_cheat":
|
|
44
|
+
# Allow setting private attributes and special attributes normally
|
|
45
|
+
super().__setattr__(key, value)
|
|
46
|
+
else:
|
|
47
|
+
self.__setitem__(key, value)
|
|
48
|
+
|
|
49
|
+
def get(self, key: str, default: _T = None) -> str | _T: # type: ignore[assignment]
|
|
50
|
+
return super().get(key.upper().replace("_", "-"), default)
|
|
51
|
+
|
|
52
|
+
def add(self, key: str, value: str) -> None:
|
|
53
|
+
"""Add a header. This expects a string with a colon separating the key and value."""
|
|
54
|
+
setattr(self, key, value)
|
|
@@ -5,7 +5,9 @@ from abc import ABC, abstractmethod
|
|
|
5
5
|
from typing import TYPE_CHECKING, Any
|
|
6
6
|
from urllib.parse import parse_qs
|
|
7
7
|
|
|
8
|
-
from clearskies import configs, configurable
|
|
8
|
+
from clearskies import configs, configurable
|
|
9
|
+
|
|
10
|
+
from .headers import Headers
|
|
9
11
|
|
|
10
12
|
if TYPE_CHECKING:
|
|
11
13
|
from clearskies import typing
|
|
@@ -14,21 +16,18 @@ if TYPE_CHECKING:
|
|
|
14
16
|
class InputOutput(ABC, configurable.Configurable):
|
|
15
17
|
"""Manage the request and response to the client."""
|
|
16
18
|
|
|
17
|
-
response_headers
|
|
18
|
-
request_headers
|
|
19
|
+
response_headers = configs.Headers(default=Headers())
|
|
20
|
+
request_headers = configs.Headers(default=Headers())
|
|
19
21
|
query_parameters = configs.AnyDict(default={})
|
|
20
22
|
routing_data = configs.StringDict(default={})
|
|
21
23
|
authorization_data = configs.AnyDict(default={})
|
|
24
|
+
request_method = configs.Select(["GET", "POST", "PATCH", "OPTIONS", "DELETE", "SEARCH"], default="GET")
|
|
22
25
|
|
|
23
26
|
_body_as_json: dict[str, Any] | list[Any] | None = {}
|
|
24
27
|
_body_loaded_as_json = False
|
|
25
28
|
|
|
26
29
|
def __init__(self):
|
|
27
|
-
self.response_headers =
|
|
28
|
-
self.request_headers = input_outputs.Headers(self.get_request_headers())
|
|
29
|
-
self.query_parameters = {key: val[0] for (key, val) in parse_qs(self.get_query_string()).items()}
|
|
30
|
-
self.authorization_data = {}
|
|
31
|
-
self.routing_data = {}
|
|
30
|
+
self.response_headers = Headers()
|
|
32
31
|
self.finalize_and_validate_configuration()
|
|
33
32
|
|
|
34
33
|
@abstractmethod
|
|
@@ -65,41 +64,17 @@ class InputOutput(ABC, configurable.Configurable):
|
|
|
65
64
|
self._body_as_json = None
|
|
66
65
|
return self._body_as_json
|
|
67
66
|
|
|
68
|
-
@abstractmethod
|
|
69
|
-
def get_request_method(self) -> str:
|
|
70
|
-
"""Return the request method set by the client."""
|
|
71
|
-
pass
|
|
72
|
-
|
|
73
|
-
@abstractmethod
|
|
74
|
-
def get_script_name(self) -> str:
|
|
75
|
-
"""Return the script name, e.g. the path requested."""
|
|
76
|
-
pass
|
|
77
|
-
|
|
78
|
-
@abstractmethod
|
|
79
|
-
def get_path_info(self) -> str:
|
|
80
|
-
"""Return the path info for the request."""
|
|
81
|
-
pass
|
|
82
|
-
|
|
83
|
-
@abstractmethod
|
|
84
|
-
def get_query_string(self) -> str:
|
|
85
|
-
"""Return the full query string for the request (everything after the first question mark in the document URL)."""
|
|
86
|
-
pass
|
|
87
|
-
|
|
88
67
|
@abstractmethod
|
|
89
68
|
def get_client_ip(self):
|
|
90
69
|
pass
|
|
91
70
|
|
|
92
71
|
@abstractmethod
|
|
93
|
-
def
|
|
72
|
+
def get_protocol(self):
|
|
94
73
|
pass
|
|
95
74
|
|
|
75
|
+
@abstractmethod
|
|
96
76
|
def get_full_path(self) -> str:
|
|
97
|
-
|
|
98
|
-
path_info = self.get_path_info()
|
|
99
|
-
script_name = self.get_script_name()
|
|
100
|
-
if not path_info or path_info[0] != "/":
|
|
101
|
-
path_info = f"/{path_info}"
|
|
102
|
-
return f"{path_info}{script_name}".replace("//", "/")
|
|
77
|
+
pass
|
|
103
78
|
|
|
104
79
|
def context_specifics(self):
|
|
105
80
|
return {}
|
|
@@ -117,17 +92,19 @@ class InputOutput(ABC, configurable.Configurable):
|
|
|
117
92
|
|
|
118
93
|
And this function returns a dictionary with the following values:
|
|
119
94
|
|
|
120
|
-
| Key | Type | Ref
|
|
121
|
-
|
|
122
|
-
| routing_data | dict[str, str] | input_output.routing_data
|
|
123
|
-
| authorization_data | dict[str, Any] | input_output.authorization_data
|
|
124
|
-
| request_data | dict[str, Any] | None | input_output.request_data
|
|
125
|
-
| query_parameters | dict[str, Any] | input_output.query_parameters
|
|
126
|
-
| request_headers | clearskies.input_outputs.Headers | input_output.request_headers
|
|
127
|
-
| **routing_data | string | **input_output.routing_data
|
|
95
|
+
| Key | Type | Ref | Value |
|
|
96
|
+
|--------------------|----------------------------------|------------------------------------|---------------------------------------------------------------------------------|
|
|
97
|
+
| routing_data | dict[str, str] | input_output.routing_data | A dictionary of data extracted from URL path parameters. |
|
|
98
|
+
| authorization_data | dict[str, Any] | input_output.authorization_data | A dictionary containing the authorization data set by the authentication method |
|
|
99
|
+
| request_data | dict[str, Any] | None | input_output.request_data | The data sent along with the request (assuming a JSON request body) |
|
|
100
|
+
| query_parameters | dict[str, Any] | input_output.query_parameters | The query parameters |
|
|
101
|
+
| request_headers | clearskies.input_outputs.Headers | input_output.request_headers | The request headers sent by the client |
|
|
102
|
+
| **routing_data | string | **input_output.routing_data | The routing data is unpacked so keys can be fetched directly |
|
|
103
|
+
| **[varies] | varies | **input_output.context_specifics() | Any additional properties added on by the context (see your context docs) |
|
|
128
104
|
"""
|
|
129
105
|
return {
|
|
130
106
|
**self.routing_data,
|
|
107
|
+
**self.context_specifics(),
|
|
131
108
|
**{
|
|
132
109
|
"routing_data": self.routing_data,
|
|
133
110
|
"authorization_data": self.authorization_data,
|
|
@@ -2,14 +2,15 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
+
from clearskies.input_outputs.headers import Headers
|
|
5
6
|
from clearskies.input_outputs.input_output import InputOutput
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class Programmatic(InputOutput):
|
|
9
10
|
_body: str | dict[str, Any] | list[Any] = ""
|
|
10
|
-
_request_method: str = ""
|
|
11
|
-
_request_headers: dict[str, Any] = {}
|
|
12
11
|
url: str = ""
|
|
12
|
+
ip_address: str = "127.0.0.1"
|
|
13
|
+
protocol: str = "https"
|
|
13
14
|
|
|
14
15
|
def __init__(
|
|
15
16
|
self,
|
|
@@ -18,35 +19,30 @@ class Programmatic(InputOutput):
|
|
|
18
19
|
body: str | dict[str, Any] | list[Any] = "",
|
|
19
20
|
query_parameters: dict[str, Any] = {},
|
|
20
21
|
request_headers: dict[str, str] = {},
|
|
22
|
+
ip_address: str = "127.0.0.1",
|
|
23
|
+
protocol: str = "https",
|
|
21
24
|
):
|
|
22
25
|
self.url = url
|
|
23
|
-
self.
|
|
26
|
+
self.request_headers = Headers(request_headers)
|
|
27
|
+
self.query_parameters = query_parameters
|
|
28
|
+
self.ip_address = ip_address
|
|
29
|
+
self.protocol = protocol
|
|
24
30
|
self._body_loaded_as_json = True
|
|
25
31
|
self._body_as_json = None
|
|
26
|
-
self.
|
|
32
|
+
self.request_method = request_method
|
|
27
33
|
if body:
|
|
28
34
|
self._body = body
|
|
29
35
|
if isinstance(body, dict) or isinstance(body, list):
|
|
30
36
|
self._body_as_json = body
|
|
31
37
|
|
|
32
38
|
super().__init__()
|
|
33
|
-
self.query_parameters = {**query_parameters}
|
|
34
39
|
|
|
35
40
|
def respond(self, response, status_code=200):
|
|
36
41
|
return (status_code, response, self.response_headers)
|
|
37
42
|
|
|
38
|
-
def get_script_name(self):
|
|
39
|
-
return self.url
|
|
40
|
-
|
|
41
|
-
def get_path_info(self):
|
|
42
|
-
return self.url
|
|
43
|
-
|
|
44
43
|
def get_full_path(self):
|
|
45
44
|
return self.url
|
|
46
45
|
|
|
47
|
-
def get_request_method(self):
|
|
48
|
-
return self._request_method
|
|
49
|
-
|
|
50
46
|
def has_body(self):
|
|
51
47
|
return bool(self._body)
|
|
52
48
|
|
|
@@ -60,10 +56,7 @@ class Programmatic(InputOutput):
|
|
|
60
56
|
return {}
|
|
61
57
|
|
|
62
58
|
def get_client_ip(self):
|
|
63
|
-
return
|
|
64
|
-
|
|
65
|
-
def get_query_string(self):
|
|
66
|
-
return ""
|
|
59
|
+
return self.ip_address
|
|
67
60
|
|
|
68
|
-
def
|
|
69
|
-
return self.
|
|
61
|
+
def get_protocol(self):
|
|
62
|
+
return self.protocol
|