You've already forked mariadb-columnstore-engine
mirror of
https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
synced 2025-11-03 17:13:17 +03:00
Added support for Sentry in cmapi server
Support distributed request tracing -Direct dependencies now in requirements[-dev].in, pip-compile generates full requirement[-dev].txt from them
This commit is contained in:
@@ -2,13 +2,13 @@
|
|||||||
[](https://ci.columnstore.mariadb.net/mariadb-corporation/mariadb-columnstore-cmapi)
|
[](https://ci.columnstore.mariadb.net/mariadb-corporation/mariadb-columnstore-cmapi)
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
This RESTfull server enables multi-node setups for MCS.
|
This RESTful server enables multi-node setups for MCS.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
See requirements.txt file.
|
See requirements.txt file.
|
||||||
|
|
||||||
All the Python packages prerequisits are shipped with a pre-built Python enterpreter.
|
All the Python packages prerequisites are shipped with a pre-built Python interpreter.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from cherrypy.process import plugins
|
|||||||
# TODO: fix dispatcher choose logic because code executing in endpoints.py
|
# TODO: fix dispatcher choose logic because code executing in endpoints.py
|
||||||
# while import process, this cause module logger misconfiguration
|
# while import process, this cause module logger misconfiguration
|
||||||
from cmapi_server.logging_management import config_cmapi_server_logging
|
from cmapi_server.logging_management import config_cmapi_server_logging
|
||||||
|
from cmapi_server.sentry import maybe_init_sentry, register_sentry_cherrypy_tool
|
||||||
config_cmapi_server_logging()
|
config_cmapi_server_logging()
|
||||||
|
|
||||||
from cmapi_server import helpers
|
from cmapi_server import helpers
|
||||||
@@ -140,15 +141,24 @@ if __name__ == '__main__':
|
|||||||
# TODO: read cmapi config filepath as an argument
|
# TODO: read cmapi config filepath as an argument
|
||||||
helpers.cmapi_config_check()
|
helpers.cmapi_config_check()
|
||||||
|
|
||||||
|
# Init Sentry if DSN is present
|
||||||
|
sentry_active = maybe_init_sentry()
|
||||||
|
if sentry_active:
|
||||||
|
register_sentry_cherrypy_tool()
|
||||||
|
|
||||||
CertificateManager.create_self_signed_certificate_if_not_exist()
|
CertificateManager.create_self_signed_certificate_if_not_exist()
|
||||||
CertificateManager.renew_certificate()
|
CertificateManager.renew_certificate()
|
||||||
|
|
||||||
app = cherrypy.tree.mount(root=None, config=CMAPI_CONF_PATH)
|
app = cherrypy.tree.mount(root=None, config=CMAPI_CONF_PATH)
|
||||||
|
root_config = {
|
||||||
|
"request.dispatch": dispatcher,
|
||||||
|
"error_page.default": jsonify_error,
|
||||||
|
}
|
||||||
|
if sentry_active:
|
||||||
|
root_config["tools.sentry.on"] = True
|
||||||
|
|
||||||
app.config.update({
|
app.config.update({
|
||||||
'/': {
|
'/': root_config,
|
||||||
'request.dispatch': dispatcher,
|
|
||||||
'error_page.default': jsonify_error,
|
|
||||||
},
|
|
||||||
'config': {
|
'config': {
|
||||||
'path': CMAPI_CONF_PATH,
|
'path': CMAPI_CONF_PATH,
|
||||||
},
|
},
|
||||||
|
|||||||
197
cmapi/cmapi_server/sentry.py
Normal file
197
cmapi/cmapi_server/sentry.py
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import logging
|
||||||
|
import socket
|
||||||
|
|
||||||
|
import cherrypy
|
||||||
|
import sentry_sdk
|
||||||
|
from sentry_sdk.integrations.aiohttp import AioHttpIntegration
|
||||||
|
from sentry_sdk.integrations.logging import LoggingIntegration
|
||||||
|
|
||||||
|
from cmapi_server import helpers
|
||||||
|
from cmapi_server.constants import CMAPI_CONF_PATH
|
||||||
|
|
||||||
|
SENTRY_ACTIVE = False
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def maybe_init_sentry() -> bool:
|
||||||
|
"""Initialize Sentry from CMAPI configuration.
|
||||||
|
|
||||||
|
Reads config and initializes Sentry only if dsn parameter is present in corresponding section.
|
||||||
|
The initialization enables the following integrations:
|
||||||
|
- LoggingIntegration: capture warning-level logs as Sentry events and use
|
||||||
|
lower-level logs as breadcrumbs.
|
||||||
|
- AioHttpIntegration: propagate trace headers for outbound requests made
|
||||||
|
with `aiohttp`.
|
||||||
|
|
||||||
|
The function is a no-op if the DSN is missing.
|
||||||
|
|
||||||
|
Returns: True if Sentry is initialized, False otherwise.
|
||||||
|
"""
|
||||||
|
global SENTRY_ACTIVE
|
||||||
|
try:
|
||||||
|
cfg_parser = helpers.get_config_parser(CMAPI_CONF_PATH)
|
||||||
|
dsn = helpers.dequote(
|
||||||
|
cfg_parser.get('Sentry', 'dsn', fallback='').strip()
|
||||||
|
)
|
||||||
|
if not dsn:
|
||||||
|
return False
|
||||||
|
|
||||||
|
environment = helpers.dequote(
|
||||||
|
cfg_parser.get('Sentry', 'environment', fallback='development').strip()
|
||||||
|
)
|
||||||
|
traces_sample_rate_str = helpers.dequote(
|
||||||
|
cfg_parser.get('Sentry', 'traces_sample_rate', fallback='1.0').strip()
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
logger.exception('Failed to initialize Sentry.')
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
sentry_logging = LoggingIntegration(
|
||||||
|
level=logging.INFO,
|
||||||
|
event_level=logging.WARNING,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
traces_sample_rate = float(traces_sample_rate_str)
|
||||||
|
except ValueError:
|
||||||
|
logger.error('Invalid traces_sample_rate: %s', traces_sample_rate_str)
|
||||||
|
traces_sample_rate = 1.0
|
||||||
|
|
||||||
|
sentry_sdk.init(
|
||||||
|
dsn=dsn,
|
||||||
|
environment=environment,
|
||||||
|
traces_sample_rate=traces_sample_rate,
|
||||||
|
integrations=[sentry_logging, AioHttpIntegration()],
|
||||||
|
)
|
||||||
|
SENTRY_ACTIVE = True
|
||||||
|
logger.info('Sentry initialized for CMAPI via config.')
|
||||||
|
except Exception:
|
||||||
|
logger.exception('Failed to initialize Sentry.')
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info('Sentry successfully initialized.')
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _sentry_on_start_resource():
|
||||||
|
"""Start or continue a Sentry transaction for the current CherryPy request.
|
||||||
|
|
||||||
|
- Continues an incoming distributed trace using Sentry trace headers if
|
||||||
|
present; otherwise starts a new transaction with `op='http.server'`.
|
||||||
|
- Pushes the transaction into the current Sentry scope and attaches useful
|
||||||
|
request metadata as tags and context (HTTP method, path, client IP,
|
||||||
|
hostname, request ID, and a filtered subset of headers).
|
||||||
|
- Stores the transaction on the CherryPy request object for later finishing
|
||||||
|
in `_sentry_on_end_request`.
|
||||||
|
"""
|
||||||
|
if not SENTRY_ACTIVE:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
request = cherrypy.request
|
||||||
|
headers = dict(getattr(request, 'headers', {}) or {})
|
||||||
|
name = f"{request.method} {request.path_info}"
|
||||||
|
transaction = sentry_sdk.start_transaction(
|
||||||
|
op='http.server', name=name, continue_from_headers=headers
|
||||||
|
)
|
||||||
|
sentry_sdk.Hub.current.scope.set_span(transaction)
|
||||||
|
|
||||||
|
# Add request-level context/tags
|
||||||
|
scope = sentry_sdk.Hub.current.scope
|
||||||
|
scope.set_tag('http.method', request.method)
|
||||||
|
scope.set_tag('http.path', request.path_info)
|
||||||
|
scope.set_tag('client.ip', getattr(request.remote, 'ip', ''))
|
||||||
|
scope.set_tag('instance.hostname', socket.gethostname())
|
||||||
|
request_id = getattr(request, 'unique_id', None)
|
||||||
|
if request_id:
|
||||||
|
scope.set_tag('request.id', request_id)
|
||||||
|
# Optionally add headers as context without sensitive values
|
||||||
|
safe_headers = {k: v for k, v in headers.items()
|
||||||
|
if k.lower() not in {'authorization', 'x-api-key'}}
|
||||||
|
scope.set_context('headers', safe_headers)
|
||||||
|
|
||||||
|
request.sentry_transaction = transaction
|
||||||
|
except Exception:
|
||||||
|
logger.exception('Failed to start Sentry transaction.')
|
||||||
|
|
||||||
|
|
||||||
|
def _sentry_before_error_response():
|
||||||
|
"""Capture the current exception (if any) to Sentry before error response.
|
||||||
|
|
||||||
|
This hook runs when CherryPy prepares an error response. If an exception is
|
||||||
|
available in the current context, it will be sent to Sentry.
|
||||||
|
"""
|
||||||
|
if not SENTRY_ACTIVE:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
sentry_sdk.capture_exception()
|
||||||
|
except Exception:
|
||||||
|
logger.exception('Failed to capture exception to Sentry.')
|
||||||
|
|
||||||
|
|
||||||
|
def _sentry_on_end_request():
|
||||||
|
"""Finish the Sentry transaction for the current CherryPy request.
|
||||||
|
|
||||||
|
Attempts to set the HTTP status code on the active transaction and then
|
||||||
|
finishes it. If no transaction was started on this request, the function is
|
||||||
|
a no-op.
|
||||||
|
"""
|
||||||
|
if not SENTRY_ACTIVE:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
request = cherrypy.request
|
||||||
|
transaction = getattr(request, 'sentry_transaction', None)
|
||||||
|
if transaction is None:
|
||||||
|
return
|
||||||
|
status = cherrypy.response.status
|
||||||
|
try:
|
||||||
|
status_code = int(str(status).split()[0])
|
||||||
|
except Exception:
|
||||||
|
status_code = None
|
||||||
|
try:
|
||||||
|
if status_code is not None and hasattr(transaction, 'set_http_status'):
|
||||||
|
transaction.set_http_status(status_code)
|
||||||
|
except Exception:
|
||||||
|
logger.exception('Failed to set HTTP status code on Sentry transaction.')
|
||||||
|
transaction.finish()
|
||||||
|
except Exception:
|
||||||
|
logger.exception('Failed to finish Sentry transaction.')
|
||||||
|
|
||||||
|
|
||||||
|
class SentryTool(cherrypy.Tool):
|
||||||
|
"""CherryPy Tool that wires Sentry request lifecycle hooks.
|
||||||
|
|
||||||
|
The tool attaches handlers for `on_start_resource`, `before_error_response`,
|
||||||
|
and `on_end_request` in order to manage Sentry transactions and error
|
||||||
|
capture across the request lifecycle.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
cherrypy.Tool.__init__(self, 'on_start_resource', self._tool_callback, priority=50)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _tool_callback():
|
||||||
|
"""Attach Sentry lifecycle callbacks to the current CherryPy request."""
|
||||||
|
cherrypy.request.hooks.attach(
|
||||||
|
'on_start_resource', _sentry_on_start_resource, priority=50
|
||||||
|
)
|
||||||
|
cherrypy.request.hooks.attach(
|
||||||
|
'before_error_response', _sentry_before_error_response, priority=60
|
||||||
|
)
|
||||||
|
cherrypy.request.hooks.attach(
|
||||||
|
'on_end_request', _sentry_on_end_request, priority=70
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register_sentry_cherrypy_tool() -> None:
|
||||||
|
"""Register the Sentry CherryPy tool under `tools.sentry`.
|
||||||
|
|
||||||
|
This function is safe to call multiple times; failures are silently ignored
|
||||||
|
to avoid impacting the application startup.
|
||||||
|
"""
|
||||||
|
if not SENTRY_ACTIVE:
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
cherrypy.tools.sentry = SentryTool()
|
||||||
|
except Exception:
|
||||||
|
logger.exception('Failed to register Sentry CherryPy tool.')
|
||||||
|
|
||||||
70
cmapi/dev_tools/piptools.sh
Executable file
70
cmapi/dev_tools/piptools.sh
Executable file
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
cmapi_dir="$(realpath "${script_dir}/..")"
|
||||||
|
|
||||||
|
export CUSTOM_COMPILE_COMMAND="dev_tools/piptools.sh compile-all"
|
||||||
|
|
||||||
|
ensure_piptools() {
|
||||||
|
if ! command -v pip-compile >/dev/null 2>&1; then
|
||||||
|
echo "Installing pip-tools..."
|
||||||
|
python3 -m pip install --upgrade pip
|
||||||
|
python3 -m pip install pip-tools
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
compile_runtime() {
|
||||||
|
ensure_piptools
|
||||||
|
cd "${cmapi_dir}"
|
||||||
|
pip-compile --quiet --resolver=backtracking --output-file=requirements.txt requirements.in
|
||||||
|
}
|
||||||
|
|
||||||
|
compile_dev() {
|
||||||
|
ensure_piptools
|
||||||
|
cd "${cmapi_dir}"
|
||||||
|
pip-compile --quiet --resolver=backtracking --output-file=requirements-dev.txt requirements-dev.in
|
||||||
|
}
|
||||||
|
|
||||||
|
compile_all() {
|
||||||
|
compile_runtime
|
||||||
|
compile_dev
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_runtime() {
|
||||||
|
ensure_piptools
|
||||||
|
cd "${cmapi_dir}"
|
||||||
|
pip-sync requirements.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_dev() {
|
||||||
|
ensure_piptools
|
||||||
|
cd "${cmapi_dir}"
|
||||||
|
pip-sync requirements.txt requirements-dev.txt
|
||||||
|
}
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: dev_tools/piptools.sh <command>
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
compile-runtime Compile requirements.in -> requirements.txt
|
||||||
|
compile-dev Compile requirements-dev.in -> requirements-dev.txt
|
||||||
|
compile-all Compile both runtime and dev requirements (default)
|
||||||
|
sync-runtime pip-sync runtime requirements only
|
||||||
|
sync-dev pip-sync runtime + dev requirements
|
||||||
|
help Show this help
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd="${1:-compile-all}"
|
||||||
|
case "${cmd}" in
|
||||||
|
compile-runtime) compile_runtime ;;
|
||||||
|
compile-dev) compile_dev ;;
|
||||||
|
compile-all) compile_all ;;
|
||||||
|
sync-runtime) sync_runtime ;;
|
||||||
|
sync-dev) sync_dev ;;
|
||||||
|
help|--help|-h) usage ;;
|
||||||
|
*) echo "Unknown command: ${cmd}" >&2; usage; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
8
cmapi/requirements-dev.in
Normal file
8
cmapi/requirements-dev.in
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Direct, top-level development/testing dependencies
|
||||||
|
# Compile with: pip-compile --output-file=requirements-dev.txt requirements-dev.in
|
||||||
|
|
||||||
|
pytest==8.3.5
|
||||||
|
fabric==3.2.2
|
||||||
|
# Tooling
|
||||||
|
pip-tools
|
||||||
|
|
||||||
@@ -1,37 +1,69 @@
|
|||||||
# For integration tests
|
#
|
||||||
pytest==8.3.5
|
# This file is autogenerated by pip-compile with Python 3.9
|
||||||
fabric==3.2.2
|
# by the following command:
|
||||||
|
#
|
||||||
# This frozen part is autogenerated by pip-compile: pip-compile requirements-dev.txt
|
# dev_tools/piptools.sh compile-all
|
||||||
|
#
|
||||||
bcrypt==4.3.0
|
bcrypt==4.3.0
|
||||||
# via paramiko
|
# via paramiko
|
||||||
|
build==1.3.0
|
||||||
|
# via pip-tools
|
||||||
cffi==1.17.1
|
cffi==1.17.1
|
||||||
# via
|
# via
|
||||||
# cryptography
|
# cryptography
|
||||||
# pynacl
|
# pynacl
|
||||||
|
click==8.1.8
|
||||||
|
# via pip-tools
|
||||||
cryptography==45.0.5
|
cryptography==45.0.5
|
||||||
# via paramiko
|
# via paramiko
|
||||||
decorator==5.2.1
|
decorator==5.2.1
|
||||||
# via fabric
|
# via fabric
|
||||||
deprecated==1.2.18
|
deprecated==1.2.18
|
||||||
# via fabric
|
# via fabric
|
||||||
|
exceptiongroup==1.3.0
|
||||||
|
# via pytest
|
||||||
fabric==3.2.2
|
fabric==3.2.2
|
||||||
# via -r requirements-dev.txt
|
# via -r requirements-dev.in
|
||||||
|
importlib-metadata==8.7.0
|
||||||
|
# via build
|
||||||
iniconfig==2.1.0
|
iniconfig==2.1.0
|
||||||
# via pytest
|
# via pytest
|
||||||
invoke==2.2.0
|
invoke==2.2.0
|
||||||
# via fabric
|
# via fabric
|
||||||
packaging==25.0
|
packaging==25.0
|
||||||
# via pytest
|
# via
|
||||||
|
# build
|
||||||
|
# pytest
|
||||||
paramiko==3.5.1
|
paramiko==3.5.1
|
||||||
# via fabric
|
# via fabric
|
||||||
|
pip-tools==7.5.0
|
||||||
|
# via -r requirements-dev.in
|
||||||
pluggy==1.6.0
|
pluggy==1.6.0
|
||||||
# via pytest
|
# via pytest
|
||||||
pycparser==2.22
|
pycparser==2.22
|
||||||
# via cffi
|
# via cffi
|
||||||
pynacl==1.5.0
|
pynacl==1.5.0
|
||||||
# via paramiko
|
# via paramiko
|
||||||
|
pyproject-hooks==1.2.0
|
||||||
|
# via
|
||||||
|
# build
|
||||||
|
# pip-tools
|
||||||
pytest==8.3.5
|
pytest==8.3.5
|
||||||
# via -r requirements-dev.txt
|
# via -r requirements-dev.in
|
||||||
|
tomli==2.2.1
|
||||||
|
# via
|
||||||
|
# build
|
||||||
|
# pip-tools
|
||||||
|
# pytest
|
||||||
|
typing-extensions==4.14.1
|
||||||
|
# via exceptiongroup
|
||||||
|
wheel==0.45.1
|
||||||
|
# via pip-tools
|
||||||
wrapt==1.17.2
|
wrapt==1.17.2
|
||||||
# via deprecated
|
# via deprecated
|
||||||
|
zipp==3.23.0
|
||||||
|
# via importlib-metadata
|
||||||
|
|
||||||
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
|
# pip
|
||||||
|
# setuptools
|
||||||
|
|||||||
19
cmapi/requirements.in
Normal file
19
cmapi/requirements.in
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Direct, top-level runtime dependencies for cmapi
|
||||||
|
# Compile with: pip-compile --output-file=requirements.txt requirements.in
|
||||||
|
|
||||||
|
aiohttp==3.11.16
|
||||||
|
awscli==1.38.28
|
||||||
|
CherryPy==18.10.0
|
||||||
|
cryptography==43.0.3
|
||||||
|
furl==2.1.4
|
||||||
|
gsutil==5.33
|
||||||
|
lxml==5.3.2
|
||||||
|
psutil==7.0.0
|
||||||
|
pyotp==2.9.0
|
||||||
|
requests==2.32.3
|
||||||
|
# required for CherryPy RoutesDispatcher,
|
||||||
|
# but CherryPy itself has no such dependency
|
||||||
|
Routes==2.5.1
|
||||||
|
typer==0.15.2
|
||||||
|
sentry-sdk==2.34.1
|
||||||
|
|
||||||
@@ -1,78 +1,231 @@
|
|||||||
aiohttp==3.11.16
|
#
|
||||||
awscli==1.38.28
|
# This file is autogenerated by pip-compile with Python 3.9
|
||||||
CherryPy==18.10.0
|
# by the following command:
|
||||||
cryptography==43.0.3
|
#
|
||||||
furl==2.1.4
|
# dev_tools/piptools.sh compile-all
|
||||||
gsutil==5.33
|
#
|
||||||
lxml==5.3.2
|
|
||||||
psutil==7.0.0
|
|
||||||
pyotp==2.9.0
|
|
||||||
requests==2.32.3
|
|
||||||
# required for CherryPy RoutesDispatcher,
|
|
||||||
# but CherryPy itself has no such a dependency
|
|
||||||
Routes==2.5.1
|
|
||||||
typer==0.15.2
|
|
||||||
|
|
||||||
# indirect dependencies
|
|
||||||
aiohappyeyeballs==2.6.1
|
aiohappyeyeballs==2.6.1
|
||||||
|
# via aiohttp
|
||||||
|
aiohttp==3.11.16
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# google-auth
|
||||||
aiosignal==1.3.2
|
aiosignal==1.3.2
|
||||||
|
# via aiohttp
|
||||||
argcomplete==3.6.2
|
argcomplete==3.6.2
|
||||||
|
# via gsutil
|
||||||
async-timeout==5.0.1
|
async-timeout==5.0.1
|
||||||
|
# via aiohttp
|
||||||
attrs==25.3.0
|
attrs==25.3.0
|
||||||
|
# via aiohttp
|
||||||
autocommand==2.2.2
|
autocommand==2.2.2
|
||||||
backports.tarfile==1.2.0
|
# via jaraco-text
|
||||||
|
awscli==1.38.28
|
||||||
|
# via -r requirements.in
|
||||||
|
backports-tarfile==1.2.0
|
||||||
|
# via jaraco-context
|
||||||
boto==2.49.0
|
boto==2.49.0
|
||||||
|
# via gcs-oauth2-boto-plugin
|
||||||
botocore==1.37.28
|
botocore==1.37.28
|
||||||
|
# via
|
||||||
|
# awscli
|
||||||
|
# s3transfer
|
||||||
cachetools==5.5.2
|
cachetools==5.5.2
|
||||||
|
# via google-auth
|
||||||
certifi==2025.1.31
|
certifi==2025.1.31
|
||||||
|
# via
|
||||||
|
# requests
|
||||||
|
# sentry-sdk
|
||||||
cffi==1.17.1
|
cffi==1.17.1
|
||||||
|
# via cryptography
|
||||||
charset-normalizer==3.4.1
|
charset-normalizer==3.4.1
|
||||||
|
# via requests
|
||||||
cheroot==10.0.1
|
cheroot==10.0.1
|
||||||
|
# via cherrypy
|
||||||
|
cherrypy==18.10.0
|
||||||
|
# via -r requirements.in
|
||||||
click==8.1.8
|
click==8.1.8
|
||||||
|
# via typer
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
|
# via awscli
|
||||||
crcmod==1.7
|
crcmod==1.7
|
||||||
|
# via gsutil
|
||||||
|
cryptography==43.0.3
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# pyopenssl
|
||||||
docutils==0.16
|
docutils==0.16
|
||||||
|
# via awscli
|
||||||
fasteners==0.19
|
fasteners==0.19
|
||||||
|
# via
|
||||||
|
# google-apitools
|
||||||
|
# gsutil
|
||||||
frozenlist==1.5.0
|
frozenlist==1.5.0
|
||||||
|
# via
|
||||||
|
# aiohttp
|
||||||
|
# aiosignal
|
||||||
|
furl==2.1.4
|
||||||
|
# via -r requirements.in
|
||||||
gcs-oauth2-boto-plugin==3.2
|
gcs-oauth2-boto-plugin==3.2
|
||||||
|
# via gsutil
|
||||||
google-apitools==0.5.32
|
google-apitools==0.5.32
|
||||||
google-auth==2.17.0
|
# via gsutil
|
||||||
|
google-auth[aiohttp]==2.17.0
|
||||||
|
# via
|
||||||
|
# gcs-oauth2-boto-plugin
|
||||||
|
# google-auth-httplib2
|
||||||
|
# gsutil
|
||||||
google-auth-httplib2==0.2.0
|
google-auth-httplib2==0.2.0
|
||||||
|
# via
|
||||||
|
# gcs-oauth2-boto-plugin
|
||||||
|
# gsutil
|
||||||
google-reauth==0.1.1
|
google-reauth==0.1.1
|
||||||
|
# via
|
||||||
|
# gcs-oauth2-boto-plugin
|
||||||
|
# gsutil
|
||||||
|
gsutil==5.33
|
||||||
|
# via -r requirements.in
|
||||||
httplib2==0.20.4
|
httplib2==0.20.4
|
||||||
|
# via
|
||||||
|
# gcs-oauth2-boto-plugin
|
||||||
|
# google-apitools
|
||||||
|
# google-auth-httplib2
|
||||||
|
# gsutil
|
||||||
|
# oauth2client
|
||||||
idna==3.10
|
idna==3.10
|
||||||
jaraco.collections==5.1.0
|
# via
|
||||||
jaraco.context==6.0.1
|
# requests
|
||||||
jaraco.functools==4.1.0
|
# yarl
|
||||||
jaraco.text==4.0.0
|
jaraco-collections==5.1.0
|
||||||
|
# via cherrypy
|
||||||
|
jaraco-context==6.0.1
|
||||||
|
# via jaraco-text
|
||||||
|
jaraco-functools==4.1.0
|
||||||
|
# via
|
||||||
|
# cheroot
|
||||||
|
# jaraco-text
|
||||||
|
# tempora
|
||||||
|
jaraco-text==4.0.0
|
||||||
|
# via jaraco-collections
|
||||||
jmespath==1.0.1
|
jmespath==1.0.1
|
||||||
|
# via botocore
|
||||||
|
lxml==5.3.2
|
||||||
|
# via -r requirements.in
|
||||||
markdown-it-py==3.0.0
|
markdown-it-py==3.0.0
|
||||||
|
# via rich
|
||||||
mdurl==0.1.2
|
mdurl==0.1.2
|
||||||
|
# via markdown-it-py
|
||||||
monotonic==1.6
|
monotonic==1.6
|
||||||
|
# via gsutil
|
||||||
more-itertools==10.6.0
|
more-itertools==10.6.0
|
||||||
|
# via
|
||||||
|
# cheroot
|
||||||
|
# cherrypy
|
||||||
|
# jaraco-functools
|
||||||
|
# jaraco-text
|
||||||
multidict==6.3.2
|
multidict==6.3.2
|
||||||
|
# via
|
||||||
|
# aiohttp
|
||||||
|
# yarl
|
||||||
oauth2client==4.1.3
|
oauth2client==4.1.3
|
||||||
|
# via
|
||||||
|
# gcs-oauth2-boto-plugin
|
||||||
|
# google-apitools
|
||||||
orderedmultidict==1.0.1
|
orderedmultidict==1.0.1
|
||||||
|
# via furl
|
||||||
portend==3.2.0
|
portend==3.2.0
|
||||||
|
# via cherrypy
|
||||||
propcache==0.3.1
|
propcache==0.3.1
|
||||||
|
# via
|
||||||
|
# aiohttp
|
||||||
|
# yarl
|
||||||
|
psutil==7.0.0
|
||||||
|
# via -r requirements.in
|
||||||
pyasn1==0.6.1
|
pyasn1==0.6.1
|
||||||
pyasn1_modules==0.4.2
|
# via
|
||||||
|
# oauth2client
|
||||||
|
# pyasn1-modules
|
||||||
|
# rsa
|
||||||
|
pyasn1-modules==0.4.2
|
||||||
|
# via
|
||||||
|
# google-auth
|
||||||
|
# oauth2client
|
||||||
pycparser==2.22
|
pycparser==2.22
|
||||||
Pygments==2.19.1
|
# via cffi
|
||||||
pyOpenSSL==24.2.1
|
pygments==2.19.1
|
||||||
|
# via rich
|
||||||
|
pyopenssl==24.2.1
|
||||||
|
# via
|
||||||
|
# gcs-oauth2-boto-plugin
|
||||||
|
# gsutil
|
||||||
|
pyotp==2.9.0
|
||||||
|
# via -r requirements.in
|
||||||
pyparsing==3.2.3
|
pyparsing==3.2.3
|
||||||
|
# via httplib2
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
|
# via
|
||||||
|
# botocore
|
||||||
|
# tempora
|
||||||
pyu2f==0.1.5
|
pyu2f==0.1.5
|
||||||
PyYAML==6.0.2
|
# via google-reauth
|
||||||
repoze.lru==0.7
|
pyyaml==6.0.2
|
||||||
|
# via awscli
|
||||||
|
repoze-lru==0.7
|
||||||
|
# via routes
|
||||||
|
requests==2.32.3
|
||||||
|
# via
|
||||||
|
# -r requirements.in
|
||||||
|
# google-auth
|
||||||
retry-decorator==1.1.1
|
retry-decorator==1.1.1
|
||||||
|
# via
|
||||||
|
# gcs-oauth2-boto-plugin
|
||||||
|
# gsutil
|
||||||
rich==14.0.0
|
rich==14.0.0
|
||||||
|
# via typer
|
||||||
|
routes==2.5.1
|
||||||
|
# via -r requirements.in
|
||||||
rsa==4.7.2
|
rsa==4.7.2
|
||||||
|
# via
|
||||||
|
# awscli
|
||||||
|
# gcs-oauth2-boto-plugin
|
||||||
|
# google-auth
|
||||||
|
# oauth2client
|
||||||
s3transfer==0.11.4
|
s3transfer==0.11.4
|
||||||
|
# via awscli
|
||||||
|
sentry-sdk==2.34.1
|
||||||
|
# via -r requirements.in
|
||||||
shellingham==1.5.4
|
shellingham==1.5.4
|
||||||
|
# via typer
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
|
# via
|
||||||
|
# furl
|
||||||
|
# gcs-oauth2-boto-plugin
|
||||||
|
# google-apitools
|
||||||
|
# google-auth
|
||||||
|
# gsutil
|
||||||
|
# oauth2client
|
||||||
|
# orderedmultidict
|
||||||
|
# python-dateutil
|
||||||
|
# pyu2f
|
||||||
|
# routes
|
||||||
tempora==5.8.0
|
tempora==5.8.0
|
||||||
typing_extensions==4.13.1
|
# via portend
|
||||||
|
typer==0.15.2
|
||||||
|
# via -r requirements.in
|
||||||
|
typing-extensions==4.13.1
|
||||||
|
# via
|
||||||
|
# multidict
|
||||||
|
# rich
|
||||||
|
# typer
|
||||||
urllib3==1.26.20
|
urllib3==1.26.20
|
||||||
|
# via
|
||||||
|
# botocore
|
||||||
|
# requests
|
||||||
|
# sentry-sdk
|
||||||
yarl==1.19.0
|
yarl==1.19.0
|
||||||
zc.lockfile==3.0.post1
|
# via aiohttp
|
||||||
|
zc-lockfile==3.0.post1
|
||||||
|
# via cherrypy
|
||||||
|
|
||||||
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
|
# setuptools
|
||||||
|
|||||||
Reference in New Issue
Block a user