diff --git a/.coveragerc36 b/.coveragerc36 index 8642882ab1..e0c19bb634 100644 --- a/.coveragerc36 +++ b/.coveragerc36 @@ -3,12 +3,12 @@ [run] branch = true -omit = +omit = /tmp/* */tests/* */.venv/* [report] -exclude_lines = +exclude_lines = if TYPE_CHECKING: diff --git a/.cursor/rules/core-architecture.mdc b/.cursor/rules/core-architecture.mdc index 885773f16d..0af65f4815 100644 --- a/.cursor/rules/core-architecture.mdc +++ b/.cursor/rules/core-architecture.mdc @@ -1,6 +1,6 @@ --- -description: -globs: +description: +globs: alwaysApply: false --- # Core Architecture diff --git a/.cursor/rules/integrations-guide.mdc b/.cursor/rules/integrations-guide.mdc index 869a7f742a..6785b1522d 100644 --- a/.cursor/rules/integrations-guide.mdc +++ b/.cursor/rules/integrations-guide.mdc @@ -1,6 +1,6 @@ --- -description: -globs: +description: +globs: alwaysApply: false --- # Integrations Guide @@ -133,10 +133,10 @@ from sentry_sdk.integrations import Integration class MyIntegration(Integration): identifier = "my_integration" - + def __init__(self, param=None): self.param = param - + @staticmethod def setup_once(): # Install hooks, monkey patches, etc. diff --git a/.cursor/rules/project-overview.mdc b/.cursor/rules/project-overview.mdc index 13fad83ae7..c8c0cf77b9 100644 --- a/.cursor/rules/project-overview.mdc +++ b/.cursor/rules/project-overview.mdc @@ -1,6 +1,6 @@ --- -description: -globs: +description: +globs: alwaysApply: false --- # Sentry Python SDK - Project Overview diff --git a/.cursor/rules/quick-reference.mdc b/.cursor/rules/quick-reference.mdc index 453869fa83..ef90c22f78 100644 --- a/.cursor/rules/quick-reference.mdc +++ b/.cursor/rules/quick-reference.mdc @@ -1,6 +1,6 @@ --- -description: -globs: +description: +globs: alwaysApply: false --- # Quick Reference @@ -44,7 +44,7 @@ tox -e py3.12-django-v5.2.3 ### Code Quality -Our `linters` tox environment runs `black` for formatting, `flake8` for linting and `mypy` for type checking. +Our `linters` tox environment runs `ruff-format` for formatting, `ruff-check` for linting and `mypy` for type checking. ```bash tox -e linters diff --git a/.cursor/rules/testing-guide.mdc b/.cursor/rules/testing-guide.mdc index e336bb337a..939f1a095b 100644 --- a/.cursor/rules/testing-guide.mdc +++ b/.cursor/rules/testing-guide.mdc @@ -1,6 +1,6 @@ --- -description: -globs: +description: +globs: alwaysApply: false --- # Testing Guide @@ -65,10 +65,10 @@ def test_flask_integration(sentry_init, capture_events): # Test setup sentry_init(integrations=[FlaskIntegration()]) events = capture_events() - + # Test execution # ... test code ... - + # Assertions assert len(events) == 1 assert events[0]["exception"]["values"][0]["type"] == "ValueError" diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..147eaebfe8 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,3 @@ +# Formatting commits to ignore in git blame +afea4a017bf13f78e82f725ea9d6a56a8e02cb34 +23a340a9dca60eea36de456def70c00952a33556 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9787e136bb..7ec62a8f37 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,31 +1,9 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.13.2 hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - -- repo: https://github.com/psf/black - rev: 24.1.0 - hooks: - - id: black - exclude: ^(.*_pb2.py|.*_pb2_grpc.py) - -- repo: https://github.com/pycqa/flake8 - rev: 5.0.4 - hooks: - - id: flake8 - additional_dependencies: - [ - flake8-pyproject, - flake8-bugbear, - pep8-naming, - ] - -# Disabled for now, because it lists a lot of problems. -#- repo: https://github.com/pre-commit/mirrors-mypy -# rev: 'v0.931' -# hooks: -# - id: mypy + - id: ruff-check + args: [--fix] + - id: ruff-format diff --git a/codecov.yml b/codecov.yml index 086157690e..b7abcf8c86 100644 --- a/codecov.yml +++ b/codecov.yml @@ -19,9 +19,9 @@ comment: # Comments will only post when coverage changes. Furthermore, if a comment # already exists, and a newer commit results in no coverage change for the # entire pull, the comment will be deleted. - require_changes: true + require_changes: true require_base: true # must have a base report to post require_head: true # must have a head report to post github_checks: - annotations: false \ No newline at end of file + annotations: false diff --git a/pyproject.toml b/pyproject.toml index 44eded7641..8e6fe345f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,19 +1,3 @@ -# -# Tool: Black -# - -[tool.black] -# 'extend-exclude' excludes files or directories in addition to the defaults -extend-exclude = ''' -# A regex preceded with ^/ will apply only to files and directories -# in the root of the project. -( - .*_pb2.py # exclude autogenerated Protocol Buffer files anywhere in the project - | .*_pb2_grpc.py # exclude autogenerated Protocol Buffer files anywhere in the project -) -''' - - # # Tool: Coverage # @@ -196,29 +180,48 @@ module = "agents.*" ignore_missing_imports = true # -# Tool: Flake8 +# Tool: Ruff (linting and formatting) # -[tool.flake8] -extend-ignore = [ - # Handled by black (Whitespace before ':' -- handled by black) - "E203", - # Handled by black (Line too long) - "E501", - # Sometimes not possible due to execution order (Module level import is not at top of file) - "E402", - # I don't care (Do not assign a lambda expression, use a def) - "E731", - # does not apply to Python 2 (redundant exception types by flake8-bugbear) - "B014", - # I don't care (Lowercase imported as non-lowercase by pep8-naming) - "N812", - # is a worse version of and conflicts with B902 (first argument of a classmethod should be named cls) - "N804", +[tool.ruff] +# Target Python 3.7+ (minimum version supported by ruff) +target-version = "py37" + +# Exclude files and directories +extend-exclude = [ + "*_pb2.py", # Protocol Buffer files (covers all pb2 files including grpc_test_service_pb2.py) + "*_pb2_grpc.py", # Protocol Buffer files (covers all pb2_grpc files including grpc_test_service_pb2_grpc.py) + "checkouts", # From flake8 + "lol*", # From flake8 ] -extend-exclude = ["checkouts", "lol*"] -exclude = [ - # gRCP generated files - "grpc_test_service_pb2.py", - "grpc_test_service_pb2_grpc.py", + +[tool.ruff.lint] +# Match flake8's default rule selection exactly +# Flake8 by default only enables E and W (pycodestyle) + F (pyflakes) +select = [ + "E", # pycodestyle errors (same as flake8 default) + "W", # pycodestyle warnings (same as flake8 default) + "F", # Pyflakes (same as flake8 default) + # Note: B and N rules are NOT enabled by default in flake8 + # They were only active through the plugins, which may not have been fully enabled ] + +# Use ONLY the same ignores as the original flake8 config + compatibility for this codebase +ignore = [ + "E203", # Whitespace before ':' + "E501", # Line too long + "E402", # Module level import not at top of file + "E731", # Do not assign a lambda expression, use a def + "B014", # Redundant exception types + "N812", # Lowercase imported as non-lowercase + "N804", # First argument of classmethod should be named cls + + # Additional ignores for codebase compatibility + "F401", # Unused imports - many in TYPE_CHECKING blocks used for type comments + "E721", # Use isinstance instead of type() == - existing pattern in this codebase +] + +[tool.ruff.format] +# ruff format already excludes the same files as specified in extend-exclude +# Ensure Python 3.7 compatibility - avoid using Python 3.9+ syntax features +skip-magic-trailing-comma = false diff --git a/requirements-linting.txt b/requirements-linting.txt index 20db2151d0..1cc8274795 100644 --- a/requirements-linting.txt +++ b/requirements-linting.txt @@ -1,9 +1,5 @@ mypy -black -flake8==5.0.4 -flake8-pyproject # Flake8 plugin to support configuration in pyproject.toml -flake8-bugbear # Flake8 plugin -pep8-naming # Flake8 plugin +ruff types-certifi types-protobuf types-gevent diff --git a/scripts/build_aws_lambda_layer.py b/scripts/build_aws_lambda_layer.py index a7e2397546..a1078f4e19 100644 --- a/scripts/build_aws_lambda_layer.py +++ b/scripts/build_aws_lambda_layer.py @@ -75,8 +75,7 @@ def create_init_serverless_sdk_package(self): sentry-python-serverless zip """ serverless_sdk_path = ( - f"{self.python_site_packages}/sentry_sdk/" - f"integrations/init_serverless_sdk" + f"{self.python_site_packages}/sentry_sdk/integrations/init_serverless_sdk" ) if not os.path.exists(serverless_sdk_path): os.makedirs(serverless_sdk_path) diff --git a/scripts/populate_tox/populate_tox.py b/scripts/populate_tox/populate_tox.py index 4bb4d78231..d625c1da72 100644 --- a/scripts/populate_tox/populate_tox.py +++ b/scripts/populate_tox/populate_tox.py @@ -601,7 +601,7 @@ def _add_python_versions_to_release( def _transform_target_python_versions( - python_versions: Union[str, dict[str, str], None] + python_versions: Union[str, dict[str, str], None], ) -> Union[SpecifierSet, dict[SpecifierSet, SpecifierSet], None]: """Wrap the contents of the `python` key in SpecifierSets.""" if not python_versions: @@ -711,9 +711,9 @@ def main(fail_on_changes: bool = False) -> None: name = _normalize_name(release["info"]["name"]) version = release["info"]["version"] CACHE[name][version] = release - CACHE[name][version][ - "_accessed" - ] = False # for cleaning up unused cache entries + CACHE[name][version]["_accessed"] = ( + False # for cleaning up unused cache entries + ) # Process packages packages = defaultdict(list) diff --git a/scripts/populate_tox/releases.jsonl b/scripts/populate_tox/releases.jsonl index c68fe87734..fa24b089cd 100644 --- a/scripts/populate_tox/releases.jsonl +++ b/scripts/populate_tox/releases.jsonl @@ -46,7 +46,7 @@ {"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7"], "name": "boto3", "requires_python": "", "version": "1.12.49", "yanked": false}} {"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9"], "name": "boto3", "requires_python": ">= 3.6", "version": "1.20.54", "yanked": false}} {"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9"], "name": "boto3", "requires_python": ">= 3.7", "version": "1.28.85", "yanked": false}} -{"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.9"], "name": "boto3", "requires_python": ">=3.9", "version": "1.40.38", "yanked": false}} +{"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Natural Language :: English", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.9"], "name": "boto3", "requires_python": ">=3.9", "version": "1.40.39", "yanked": false}} {"info": {"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 2.5", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries", "Topic :: Internet :: WWW/HTTP :: HTTP Servers", "Topic :: Internet :: WWW/HTTP :: WSGI", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", "Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware", "Topic :: Internet :: WWW/HTTP :: WSGI :: Server", "Topic :: Software Development :: Libraries :: Application Frameworks"], "name": "bottle", "requires_python": "", "version": "0.12.25", "yanked": false}} {"info": {"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries", "Topic :: Internet :: WWW/HTTP :: HTTP Servers", "Topic :: Internet :: WWW/HTTP :: WSGI", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", "Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware", "Topic :: Internet :: WWW/HTTP :: WSGI :: Server", "Topic :: Software Development :: Libraries :: Application Frameworks"], "name": "bottle", "requires_python": null, "version": "0.13.4", "yanked": false}} {"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Object Brokering", "Topic :: System :: Distributed Computing"], "name": "celery", "requires_python": ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", "version": "4.4.7", "yanked": false}} @@ -77,7 +77,7 @@ {"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8"], "name": "grpcio", "requires_python": "", "version": "1.32.0", "yanked": false}} {"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9"], "name": "grpcio", "requires_python": ">=3.6", "version": "1.47.5", "yanked": false}} {"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9"], "name": "grpcio", "requires_python": ">=3.7", "version": "1.62.3", "yanked": false}} -{"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.9"], "name": "grpcio", "requires_python": ">=3.9", "version": "1.75.0", "yanked": false}} +{"info": {"classifiers": ["Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3.9"], "name": "grpcio", "requires_python": ">=3.9", "version": "1.75.1", "yanked": false}} {"info": {"classifiers": ["Development Status :: 4 - Beta", "Environment :: Web Environment", "Framework :: AsyncIO", "Framework :: Trio", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Internet :: WWW/HTTP"], "name": "httpx", "requires_python": ">=3.6", "version": "0.16.1", "yanked": false}} {"info": {"classifiers": ["Development Status :: 4 - Beta", "Environment :: Web Environment", "Framework :: AsyncIO", "Framework :: Trio", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Internet :: WWW/HTTP"], "name": "httpx", "requires_python": ">=3.6", "version": "0.20.0", "yanked": false}} {"info": {"classifiers": ["Development Status :: 4 - Beta", "Environment :: Web Environment", "Framework :: AsyncIO", "Framework :: Trio", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Topic :: Internet :: WWW/HTTP"], "name": "httpx", "requires_python": ">=3.6", "version": "0.22.0", "yanked": false}} diff --git a/scripts/populate_tox/tox.jinja b/scripts/populate_tox/tox.jinja index 39d2406b84..2a33e7790d 100755 --- a/scripts/populate_tox/tox.jinja +++ b/scripts/populate_tox/tox.jinja @@ -176,11 +176,9 @@ basepython = py3.12: python3.12 py3.13: python3.13 - # Python version is pinned here because flake8 actually behaves differently - # depending on which version is used. You can patch this out to point to - # some random Python 3 binary, but then you get guaranteed mismatches with - # CI. Other tools such as mypy and black have options that pin the Python - # version. + # Python version is pinned here for consistency across environments. + # Tools like ruff and mypy have options that pin the target Python + # version (configured in pyproject.toml), ensuring consistent behavior. linters: python3.12 commands = @@ -196,6 +194,6 @@ commands = [testenv:linters] commands = - flake8 tests sentry_sdk - black --check tests sentry_sdk + ruff check tests sentry_sdk + ruff format --check tests sentry_sdk mypy sentry_sdk diff --git a/scripts/ready_yet/main.py b/scripts/ready_yet/main.py index bba97d0c98..1f94adeb70 100644 --- a/scripts/ready_yet/main.py +++ b/scripts/ready_yet/main.py @@ -56,7 +56,7 @@ def main(): Check if libraries in our tox.ini are ready for Python version defined in `PYTHON_VERSION`. """ print(f"Checking libs from tox.ini for Python {PYTHON_VERSION} compatibility:") - + ready = set() not_ready = set() not_found = set() diff --git a/scripts/ready_yet/run.sh b/scripts/ready_yet/run.sh index f32bd7bdda..f872079c87 100755 --- a/scripts/ready_yet/run.sh +++ b/scripts/ready_yet/run.sh @@ -3,7 +3,7 @@ # exit on first error set -xe -reset +reset # create and activate virtual environment python -m venv .venv @@ -13,4 +13,4 @@ source .venv/bin/activate python -m pip install -r requirements.txt # Run the script -python main.py \ No newline at end of file +python main.py diff --git a/scripts/test-lambda-locally/README.md b/scripts/test-lambda-locally/README.md index 115927cc2b..2c02d6301f 100644 --- a/scripts/test-lambda-locally/README.md +++ b/scripts/test-lambda-locally/README.md @@ -1,28 +1,28 @@ # Test AWS Lambda functions locally -An easy way to run an AWS Lambda function with the Sentry SDK locally. +An easy way to run an AWS Lambda function with the Sentry SDK locally. -This is a small helper to create a AWS Lambda function that includes the +This is a small helper to create a AWS Lambda function that includes the currently checked out Sentry SDK and runs it in a local AWS Lambda environment. -Currently only embedding the Sentry SDK into the Lambda function package -is supported. Adding the SDK as Lambda Layer is not possible at the moment. +Currently only embedding the Sentry SDK into the Lambda function package +is supported. Adding the SDK as Lambda Layer is not possible at the moment. ## Prerequisites - Set `SENTRY_DSN` environment variable. The Lambda function will use this DSN. -- You need to have Docker installed and running. +- You need to have Docker installed and running. ## Run Lambda function -- Update `lambda_function.py` to include your test code. +- Update `lambda_function.py` to include your test code. - Run `./deploy-lambda-locally.sh`. This will: - Install [AWS SAM](https://aws.amazon.com/serverless/sam/) in a virtual Python environment - Create a lambda function package in `package/` that includes - The currently checked out Sentry SDK - All dependencies of the Sentry SDK (certifi and urllib3) - - The actual function defined in `lamdba_function.py`. + - The actual function defined in `lamdba_function.py`. - Zip everything together into lambda_deployment_package.zip - Run a local Lambda environment that serves that Lambda function. - Point your browser to `http://127.0.0.1:3000` to access your Lambda function. - - Currently GET and POST requests are possible. This is defined in `template.yaml`. \ No newline at end of file + - Currently GET and POST requests are possible. This is defined in `template.yaml`. diff --git a/scripts/test-lambda-locally/deploy-lambda-locally.sh b/scripts/test-lambda-locally/deploy-lambda-locally.sh index 495c1259dc..5da4fee6ba 100755 --- a/scripts/test-lambda-locally/deploy-lambda-locally.sh +++ b/scripts/test-lambda-locally/deploy-lambda-locally.sh @@ -13,9 +13,9 @@ fi uv sync # Create a deployment package of the lambda function in `lambda_function.py`. -rm -rf package && mkdir -p package +rm -rf package && mkdir -p package pip install ../../../sentry-python -t package/ --upgrade -cp lambda_function.py package/ +cp lambda_function.py package/ cd package && zip -r ../lambda_deployment_package.zip . && cd .. # Start the local Lambda server with the new function (defined in template.yaml) diff --git a/scripts/test-lambda-locally/lambda_function.py b/scripts/test-lambda-locally/lambda_function.py index ceab090499..fdaa2160eb 100644 --- a/scripts/test-lambda-locally/lambda_function.py +++ b/scripts/test-lambda-locally/lambda_function.py @@ -5,21 +5,22 @@ from sentry_sdk.integrations.aws_lambda import AwsLambdaIntegration from sentry_sdk.integrations.logging import LoggingIntegration + def lambda_handler(event, context): sentry_sdk.init( dsn=os.environ.get("SENTRY_DSN"), attach_stacktrace=True, integrations=[ LoggingIntegration(level=logging.INFO, event_level=logging.ERROR), - AwsLambdaIntegration(timeout_warning=True) + AwsLambdaIntegration(timeout_warning=True), ], traces_sample_rate=1.0, debug=True, ) try: - my_dict = {"a" : "test"} - value = my_dict["b"] # This should raise exception + my_dict = {"a": "test"} + _ = my_dict["b"] # This should raise exception except: logging.exception("Key Does not Exists") raise diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index c45d5e2f4f..c06043ebe2 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -178,9 +178,7 @@ class BaseClient: def __init__(self, options=None): # type: (Optional[Dict[str, Any]]) -> None - self.options = ( - options if options is not None else DEFAULT_OPTIONS - ) # type: Dict[str, Any] + self.options = options if options is not None else DEFAULT_OPTIONS # type: Dict[str, Any] self.transport = None # type: Optional[Transport] self.monitor = None # type: Optional[Monitor] @@ -956,7 +954,7 @@ def _capture_experimental_log(self, log): debug = self.options.get("debug", False) if debug: logger.debug( - f'[Sentry Logs] [{log.get("severity_text")}] {log.get("body")}' + f"[Sentry Logs] [{log.get('severity_text')}] {log.get('body')}" ) before_send_log = get_before_send_log(self.options) @@ -970,7 +968,8 @@ def _capture_experimental_log(self, log): self.log_batcher.add(log) def capture_session( - self, session # type: Session + self, + session, # type: Session ): # type: (...) -> None if not session.release: @@ -991,7 +990,8 @@ def get_integration(self, name_or_class): ... def get_integration( - self, name_or_class # type: Union[str, Type[Integration]] + self, + name_or_class, # type: Union[str, Type[Integration]] ): # type: (...) -> Optional[Integration] """Returns the integration for this client by name or class. diff --git a/sentry_sdk/consts.py b/sentry_sdk/consts.py index 5b8e840f13..9e84dc3dd2 100644 --- a/sentry_sdk/consts.py +++ b/sentry_sdk/consts.py @@ -852,7 +852,6 @@ class OP: # This type exists to trick mypy and PyCharm into thinking `init` and `Client` # take these arguments (even though they take opaque **kwargs) class ClientConstructor: - def __init__( self, dsn=None, # type: Optional[str] diff --git a/sentry_sdk/envelope.py b/sentry_sdk/envelope.py index 7dbbdec5c8..d9b2c1629a 100644 --- a/sentry_sdk/envelope.py +++ b/sentry_sdk/envelope.py @@ -57,25 +57,29 @@ def description(self): ) def add_event( - self, event # type: Event + self, + event, # type: Event ): # type: (...) -> None self.add_item(Item(payload=PayloadRef(json=event), type="event")) def add_transaction( - self, transaction # type: Event + self, + transaction, # type: Event ): # type: (...) -> None self.add_item(Item(payload=PayloadRef(json=transaction), type="transaction")) def add_profile( - self, profile # type: Any + self, + profile, # type: Any ): # type: (...) -> None self.add_item(Item(payload=PayloadRef(json=profile), type="profile")) def add_profile_chunk( - self, profile_chunk # type: Any + self, + profile_chunk, # type: Any ): # type: (...) -> None self.add_item( @@ -87,13 +91,15 @@ def add_profile_chunk( ) def add_checkin( - self, checkin # type: Any + self, + checkin, # type: Any ): # type: (...) -> None self.add_item(Item(payload=PayloadRef(json=checkin), type="check_in")) def add_session( - self, session # type: Union[Session, Any] + self, + session, # type: Union[Session, Any] ): # type: (...) -> None if isinstance(session, Session): @@ -101,13 +107,15 @@ def add_session( self.add_item(Item(payload=PayloadRef(json=session), type="session")) def add_sessions( - self, sessions # type: Any + self, + sessions, # type: Any ): # type: (...) -> None self.add_item(Item(payload=PayloadRef(json=sessions), type="sessions")) def add_item( - self, item # type: Item + self, + item, # type: Item ): # type: (...) -> None self.items.append(item) @@ -133,7 +141,8 @@ def __iter__(self): return iter(self.items) def serialize_into( - self, f # type: Any + self, + f, # type: Any ): # type: (...) -> None f.write(json_dumps(self.headers)) @@ -149,7 +158,8 @@ def serialize(self): @classmethod def deserialize_from( - cls, f # type: Any + cls, + f, # type: Any ): # type: (...) -> Envelope headers = parse_json(f.readline()) @@ -163,7 +173,8 @@ def deserialize_from( @classmethod def deserialize( - cls, bytes # type: bytes + cls, + bytes, # type: bytes ): # type: (...) -> Envelope return cls.deserialize_from(io.BytesIO(bytes)) @@ -307,7 +318,8 @@ def get_transaction_event(self): return None def serialize_into( - self, f # type: Any + self, + f, # type: Any ): # type: (...) -> None headers = dict(self.headers) @@ -326,7 +338,8 @@ def serialize(self): @classmethod def deserialize_from( - cls, f # type: Any + cls, + f, # type: Any ): # type: (...) -> Optional[Item] line = f.readline().rstrip() @@ -349,7 +362,8 @@ def deserialize_from( @classmethod def deserialize( - cls, bytes # type: bytes + cls, + bytes, # type: bytes ): # type: (...) -> Optional[Item] return cls.deserialize_from(io.BytesIO(bytes)) diff --git a/sentry_sdk/feature_flags.py b/sentry_sdk/feature_flags.py index eb53acae5d..03fba9c53c 100644 --- a/sentry_sdk/feature_flags.py +++ b/sentry_sdk/feature_flags.py @@ -15,7 +15,6 @@ class FlagBuffer: - def __init__(self, capacity): # type: (int) -> None self.capacity = capacity diff --git a/sentry_sdk/hub.py b/sentry_sdk/hub.py index 7fda9202df..6f2d1bbf13 100644 --- a/sentry_sdk/hub.py +++ b/sentry_sdk/hub.py @@ -205,7 +205,8 @@ def __exit__( scope._isolation_scope.set(old_isolation_scope) def run( - self, callback # type: Callable[[], T] + self, + callback, # type: Callable[[], T] ): # type: (...) -> T """ @@ -219,7 +220,8 @@ def run( return callback() def get_integration( - self, name_or_class # type: Union[str, Type[Integration]] + self, + name_or_class, # type: Union[str, Type[Integration]] ): # type: (...) -> Any """ @@ -277,7 +279,8 @@ def last_event_id(self): return self._last_event_id def bind_client( - self, new # type: Optional[BaseClient] + self, + new, # type: Optional[BaseClient] ): # type: (...) -> None """ @@ -430,7 +433,7 @@ def start_transaction( transaction=None, instrumenter=INSTRUMENTER.SENTRY, custom_sampling_context=None, - **kwargs + **kwargs, ): # type: (Optional[Transaction], str, Optional[SamplingContext], Unpack[TransactionKwargs]) -> Union[Transaction, NoOpSpan] """ @@ -487,14 +490,16 @@ def continue_trace(self, environ_or_headers, op=None, name=None, source=None): @overload def push_scope( - self, callback=None # type: Optional[None] + self, + callback=None, # type: Optional[None] ): # type: (...) -> ContextManager[Scope] pass @overload def push_scope( # noqa: F811 - self, callback # type: Callable[[Scope], None] + self, + callback, # type: Callable[[Scope], None] ): # type: (...) -> None pass @@ -540,14 +545,16 @@ def pop_scope_unsafe(self): @overload def configure_scope( - self, callback=None # type: Optional[None] + self, + callback=None, # type: Optional[None] ): # type: (...) -> ContextManager[Scope] pass @overload def configure_scope( # noqa: F811 - self, callback # type: Callable[[Scope], None] + self, + callback, # type: Callable[[Scope], None] ): # type: (...) -> None pass @@ -587,7 +594,8 @@ def inner(): return inner() def start_session( - self, session_mode="application" # type: str + self, + session_mode="application", # type: str ): # type: (...) -> None """ diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index dde8128a33..28b44cc7ab 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -233,14 +233,15 @@ async def _run_app(self, scope, receive, send, asgi_version): if transaction: transaction.set_tag("asgi.type", ty) - with ( + transaction_context = ( sentry_sdk.start_transaction( transaction, custom_sampling_context={"asgi_scope": scope}, ) if transaction is not None else nullcontext() - ): + ) + with transaction_context: try: async def _sentry_wrapped_send(event): diff --git a/sentry_sdk/integrations/grpc/aio/client.py b/sentry_sdk/integrations/grpc/aio/client.py index ff3c213176..7462675a97 100644 --- a/sentry_sdk/integrations/grpc/aio/client.py +++ b/sentry_sdk/integrations/grpc/aio/client.py @@ -65,7 +65,8 @@ async def intercept_unary_unary( class SentryUnaryStreamClientInterceptor( - ClientInterceptor, UnaryStreamClientInterceptor # type: ignore + ClientInterceptor, + UnaryStreamClientInterceptor, # type: ignore ): async def intercept_unary_stream( self, diff --git a/sentry_sdk/integrations/grpc/client.py b/sentry_sdk/integrations/grpc/client.py index a5b4f9f52e..ef24821ed2 100644 --- a/sentry_sdk/integrations/grpc/client.py +++ b/sentry_sdk/integrations/grpc/client.py @@ -19,7 +19,8 @@ class ClientInterceptor( - grpc.UnaryUnaryClientInterceptor, grpc.UnaryStreamClientInterceptor # type: ignore + grpc.UnaryUnaryClientInterceptor, # type: ignore + grpc.UnaryStreamClientInterceptor, # type: ignore ): _is_intercepted = False @@ -60,9 +61,7 @@ def intercept_unary_stream(self, continuation, client_call_details, request): client_call_details ) - response = continuation( - client_call_details, request - ) # type: UnaryStreamCall + response = continuation(client_call_details, request) # type: UnaryStreamCall # Setting code on unary-stream leads to execution getting stuck # span.set_data("code", response.code().name) diff --git a/sentry_sdk/integrations/langchain.py b/sentry_sdk/integrations/langchain.py index 6bc3ceb93e..fdba26569d 100644 --- a/sentry_sdk/integrations/langchain.py +++ b/sentry_sdk/integrations/langchain.py @@ -554,7 +554,6 @@ def _simplify_langchain_tools(tools): for tool in tools: try: if isinstance(tool, dict): - if "function" in tool and isinstance(tool["function"], dict): func = tool["function"] simplified_tool = { diff --git a/sentry_sdk/integrations/launchdarkly.py b/sentry_sdk/integrations/launchdarkly.py index d3c423e7be..6dfc1958b7 100644 --- a/sentry_sdk/integrations/launchdarkly.py +++ b/sentry_sdk/integrations/launchdarkly.py @@ -44,7 +44,6 @@ def setup_once(): class LaunchDarklyHook(Hook): - @property def metadata(self): # type: () -> Metadata diff --git a/sentry_sdk/integrations/litestar.py b/sentry_sdk/integrations/litestar.py index 2be4d376e0..745a00bcba 100644 --- a/sentry_sdk/integrations/litestar.py +++ b/sentry_sdk/integrations/litestar.py @@ -222,9 +222,7 @@ async def handle_wrapper(self, scope, receive, send): return await old_handle(self, scope, receive, send) sentry_scope = sentry_sdk.get_isolation_scope() - request = scope["app"].request_class( - scope=scope, receive=receive, send=send - ) # type: Request[Any, Any] + request = scope["app"].request_class(scope=scope, receive=receive, send=send) # type: Request[Any, Any] extracted_request_data = ConnectionDataExtractor( parse_body=True, parse_query=True )(request) diff --git a/sentry_sdk/integrations/pure_eval.py b/sentry_sdk/integrations/pure_eval.py index c1c3d63871..6ac10dfe1b 100644 --- a/sentry_sdk/integrations/pure_eval.py +++ b/sentry_sdk/integrations/pure_eval.py @@ -116,7 +116,9 @@ def start(n): return (n.lineno, n.col_offset) nodes_before_stmt = [ - node for node in nodes if start(node) < stmt.last_token.end # type: ignore + node + for node in nodes + if start(node) < stmt.last_token.end # type: ignore ] if nodes_before_stmt: # The position of the last node before or in the statement diff --git a/sentry_sdk/integrations/spark/spark_driver.py b/sentry_sdk/integrations/spark/spark_driver.py index fac985357f..b22dc2c807 100644 --- a/sentry_sdk/integrations/spark/spark_driver.py +++ b/sentry_sdk/integrations/spark/spark_driver.py @@ -158,7 +158,8 @@ def onExecutorBlacklisted(self, executorBlacklisted): # noqa: N802,N803 pass def onExecutorBlacklistedForStage( # noqa: N802 - self, executorBlacklistedForStage # noqa: N803 + self, + executorBlacklistedForStage, # noqa: N803 ): # type: (Any) -> None pass diff --git a/sentry_sdk/integrations/sqlalchemy.py b/sentry_sdk/integrations/sqlalchemy.py index 068d373053..0e039f93f3 100644 --- a/sentry_sdk/integrations/sqlalchemy.py +++ b/sentry_sdk/integrations/sqlalchemy.py @@ -64,9 +64,7 @@ def _before_cursor_execute( @ensure_integration_enabled(SqlalchemyIntegration) def _after_cursor_execute(conn, cursor, statement, parameters, context, *args): # type: (Any, Any, Any, Any, Any, *Any) -> None - ctx_mgr = getattr( - context, "_sentry_sql_span_manager", None - ) # type: Optional[ContextManager[Any]] + ctx_mgr = getattr(context, "_sentry_sql_span_manager", None) # type: Optional[ContextManager[Any]] if ctx_mgr is not None: context._sentry_sql_span_manager = None @@ -92,9 +90,7 @@ def _handle_error(context, *args): # _after_cursor_execute does not get called for crashing SQL stmts. Judging # from SQLAlchemy codebase it does seem like any error coming into this # handler is going to be fatal. - ctx_mgr = getattr( - execution_context, "_sentry_sql_span_manager", None - ) # type: Optional[ContextManager[Any]] + ctx_mgr = getattr(execution_context, "_sentry_sql_span_manager", None) # type: Optional[ContextManager[Any]] if ctx_mgr is not None: execution_context._sentry_sql_span_manager = None diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index c7ce40618b..f1a0e360bb 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -103,9 +103,7 @@ def __init__( self.http_methods_to_capture = tuple(map(str.upper, http_methods_to_capture)) if isinstance(failed_request_status_codes, Set): - self.failed_request_status_codes = ( - failed_request_status_codes - ) # type: Container[int] + self.failed_request_status_codes = failed_request_status_codes # type: Container[int] else: warnings.warn( "Passing a list or None for failed_request_status_codes is deprecated. " diff --git a/sentry_sdk/integrations/starlite.py b/sentry_sdk/integrations/starlite.py index b402aa2184..daab82d642 100644 --- a/sentry_sdk/integrations/starlite.py +++ b/sentry_sdk/integrations/starlite.py @@ -200,9 +200,7 @@ async def handle_wrapper(self, scope, receive, send): return await old_handle(self, scope, receive, send) sentry_scope = sentry_sdk.get_isolation_scope() - request = scope["app"].request_class( - scope=scope, receive=receive, send=send - ) # type: Request[Any, Any] + request = scope["app"].request_class(scope=scope, receive=receive, send=send) # type: Request[Any, Any] extracted_request_data = ConnectionDataExtractor( parse_body=True, parse_query=True )(request) diff --git a/sentry_sdk/integrations/wsgi.py b/sentry_sdk/integrations/wsgi.py index e628e50e69..fa79ec96da 100644 --- a/sentry_sdk/integrations/wsgi.py +++ b/sentry_sdk/integrations/wsgi.py @@ -119,14 +119,15 @@ def __call__(self, environ, start_response): origin=self.span_origin, ) - with ( + transaction_context = ( sentry_sdk.start_transaction( transaction, custom_sampling_context={"wsgi_environ": environ}, ) if transaction is not None else nullcontext() - ): + ) + with transaction_context: try: response = self.app( environ, diff --git a/sentry_sdk/metrics.py b/sentry_sdk/metrics.py index 4bdbc62253..d0041114ce 100644 --- a/sentry_sdk/metrics.py +++ b/sentry_sdk/metrics.py @@ -159,7 +159,8 @@ class CounterMetric(Metric): __slots__ = ("value",) def __init__( - self, first # type: MetricValue + self, + first, # type: MetricValue ): # type: (...) -> None self.value = float(first) @@ -170,7 +171,8 @@ def weight(self): return 1 def add( - self, value # type: MetricValue + self, + value, # type: MetricValue ): # type: (...) -> None self.value += float(value) @@ -190,7 +192,8 @@ class GaugeMetric(Metric): ) def __init__( - self, first # type: MetricValue + self, + first, # type: MetricValue ): # type: (...) -> None first = float(first) @@ -207,7 +210,8 @@ def weight(self): return 5 def add( - self, value # type: MetricValue + self, + value, # type: MetricValue ): # type: (...) -> None value = float(value) @@ -232,7 +236,8 @@ class DistributionMetric(Metric): __slots__ = ("value",) def __init__( - self, first # type: MetricValue + self, + first, # type: MetricValue ): # type(...) -> None self.value = [float(first)] @@ -243,7 +248,8 @@ def weight(self): return len(self.value) def add( - self, value # type: MetricValue + self, + value, # type: MetricValue ): # type: (...) -> None self.value.append(float(value)) @@ -257,7 +263,8 @@ class SetMetric(Metric): __slots__ = ("value",) def __init__( - self, first # type: MetricValue + self, + first, # type: MetricValue ): # type: (...) -> None self.value = {first} @@ -268,7 +275,8 @@ def weight(self): return len(self.value) def add( - self, value # type: MetricValue + self, + value, # type: MetricValue ): # type: (...) -> None self.value.add(value) @@ -373,9 +381,7 @@ class LocalAggregator: def __init__(self): # type: (...) -> None - self._measurements = ( - {} - ) # type: Dict[Tuple[str, MetricTagsInternal], Tuple[float, float, int, float]] + self._measurements = {} # type: Dict[Tuple[str, MetricTagsInternal], Tuple[float, float, int, float]] def add( self, diff --git a/sentry_sdk/profiler/utils.py b/sentry_sdk/profiler/utils.py index 3554cddb5d..7d311e91f4 100644 --- a/sentry_sdk/profiler/utils.py +++ b/sentry_sdk/profiler/utils.py @@ -85,9 +85,7 @@ def get_frame_name(frame): if ( # the co_varnames start with the frame's positional arguments # and we expect the first to be `self` if its an instance method - co_varnames - and co_varnames[0] == "self" - and "self" in frame.f_locals + co_varnames and co_varnames[0] == "self" and "self" in frame.f_locals ): for cls in type(frame.f_locals["self"]).__mro__: if name in cls.__dict__: @@ -101,9 +99,7 @@ def get_frame_name(frame): if ( # the co_varnames start with the frame's positional arguments # and we expect the first to be `cls` if its a class method - co_varnames - and co_varnames[0] == "cls" - and "cls" in frame.f_locals + co_varnames and co_varnames[0] == "cls" and "cls" in frame.f_locals ): for cls in frame.f_locals["cls"].__mro__: if name in cls.__dict__: diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 3356de57a8..c871e6a467 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -894,7 +894,8 @@ def set_context( self._contexts[key] = value def remove_context( - self, key # type: str + self, + key, # type: str ): # type: (...) -> None """Removes a context.""" @@ -910,7 +911,8 @@ def set_extra( self._extras[key] = value def remove_extra( - self, key # type: str + self, + key, # type: str ): # type: (...) -> None """Removes a specific extra key.""" @@ -1321,7 +1323,8 @@ def resume_auto_session_tracking(self): self._force_auto_session_tracking = None def add_event_processor( - self, func # type: EventProcessor + self, + func, # type: EventProcessor ): # type: (...) -> None """Register a scope local event processor on the scope. diff --git a/sentry_sdk/serializer.py b/sentry_sdk/serializer.py index 6bde5c08bd..1775b1b555 100644 --- a/sentry_sdk/serializer.py +++ b/sentry_sdk/serializer.py @@ -128,9 +128,7 @@ def serialize(event, **kwargs): path = [] # type: List[Segment] meta_stack = [] # type: List[Dict[str, Any]] - keep_request_bodies = ( - kwargs.pop("max_request_body_size", None) == "always" - ) # type: bool + keep_request_bodies = kwargs.pop("max_request_body_size", None) == "always" # type: bool max_value_length = kwargs.pop("max_value_length", None) # type: Optional[int] is_vars = kwargs.pop("is_vars", False) custom_repr = kwargs.pop("custom_repr", None) # type: Callable[..., Optional[str]] diff --git a/sentry_sdk/session.py b/sentry_sdk/session.py index c1d422c115..af9551c56e 100644 --- a/sentry_sdk/session.py +++ b/sentry_sdk/session.py @@ -130,7 +130,8 @@ def update( self.status = status def close( - self, status=None # type: Optional[SessionStatus] + self, + status=None, # type: Optional[SessionStatus] ): # type: (...) -> Any if status is None and self.status == "ok": @@ -139,7 +140,8 @@ def close( self.update(status=status) def get_json_attrs( - self, with_user_info=True # type: Optional[bool] + self, + with_user_info=True, # type: Optional[bool] ): # type: (...) -> Any attrs = {} diff --git a/sentry_sdk/sessions.py b/sentry_sdk/sessions.py index 00fda23200..2bf4ee707a 100644 --- a/sentry_sdk/sessions.py +++ b/sentry_sdk/sessions.py @@ -228,7 +228,8 @@ def _thread(): return None def add_aggregate_session( - self, session # type: Session + self, + session, # type: Session ): # type: (...) -> None # NOTE on `session.did`: @@ -259,7 +260,8 @@ def add_aggregate_session( state["exited"] = state.get("exited", 0) + 1 def add_session( - self, session # type: Session + self, + session, # type: Session ): # type: (...) -> None if session.session_mode == "request": diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py index 4edda21075..a82b99ff4d 100644 --- a/sentry_sdk/tracing.py +++ b/sentry_sdk/tracing.py @@ -998,9 +998,7 @@ def finish( # For backwards compatibility, we must handle the case where `scope` # or `hub` could both either be a `Scope` or a `Hub`. - scope = self._get_scope_from_finish_args( - scope, hub - ) # type: Optional[sentry_sdk.Scope] + scope = self._get_scope_from_finish_args(scope, hub) # type: Optional[sentry_sdk.Scope] scope = scope or self.scope or sentry_sdk.get_current_scope() client = sentry_sdk.get_client() @@ -1209,8 +1207,8 @@ def _set_initial_sampling_decision(self, sampling_context): sample_rate = ( client.options["traces_sampler"](sampling_context) if callable(client.options.get("traces_sampler")) + # default inheritance behavior else ( - # default inheritance behavior sampling_context["parent_sampled"] if sampling_context["parent_sampled"] is not None else client.options["traces_sample_rate"] diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 2f3e334e3f..b81d647c6d 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -527,9 +527,7 @@ def _fill_sample_rand(self): ) return - self.dynamic_sampling_context["sample_rand"] = ( - f"{sample_rand:.6f}" # noqa: E231 - ) + self.dynamic_sampling_context["sample_rand"] = f"{sample_rand:.6f}" # noqa: E231 def _sample_rand(self): # type: () -> Optional[str] diff --git a/sentry_sdk/transport.py b/sentry_sdk/transport.py index e904081959..75384519e9 100644 --- a/sentry_sdk/transport.py +++ b/sentry_sdk/transport.py @@ -203,9 +203,7 @@ def __init__(self, options): self._disabled_until = {} # type: Dict[Optional[EventDataCategory], datetime] # We only use this Retry() class for the `get_retry_after` method it exposes self._retry = urllib3.util.Retry() - self._discarded_events = defaultdict( - int - ) # type: DefaultDict[Tuple[EventDataCategory, str], int] + self._discarded_events = defaultdict(int) # type: DefaultDict[Tuple[EventDataCategory, str], int] self._last_client_report_sent = time.time() self._pool = self._make_pool() @@ -549,7 +547,8 @@ def _request( raise NotImplementedError() def capture_envelope( - self, envelope # type: Envelope + self, + envelope, # type: Envelope ): # type: (...) -> None def send_envelope_wrapper(): @@ -862,14 +861,16 @@ class _FunctionTransport(Transport): """ def __init__( - self, func # type: Callable[[Event], None] + self, + func, # type: Callable[[Event], None] ): # type: (...) -> None Transport.__init__(self) self._func = func def capture_event( - self, event # type: Event + self, + event, # type: Event ): # type: (...) -> None self._func(event) @@ -891,9 +892,7 @@ def make_transport(options): use_http2_transport = options.get("_experiments", {}).get("transport_http2", False) # By default, we use the http transport class - transport_cls = ( - Http2Transport if use_http2_transport else HttpTransport - ) # type: Type[Transport] + transport_cls = Http2Transport if use_http2_transport else HttpTransport # type: Type[Transport] if isinstance(ref_transport, Transport): return ref_transport diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py index 3fe3ac3eec..2083fd296c 100644 --- a/sentry_sdk/utils.py +++ b/sentry_sdk/utils.py @@ -389,7 +389,8 @@ def __init__( self.client = client def get_api_url( - self, type=EndpointType.ENVELOPE # type: EndpointType + self, + type=EndpointType.ENVELOPE, # type: EndpointType ): # type: (...) -> str """Returns the API url for storing events.""" @@ -850,7 +851,9 @@ def exceptions_from_error( parent_id = exception_id exception_id += 1 - should_supress_context = hasattr(exc_value, "__suppress_context__") and exc_value.__suppress_context__ # type: ignore + should_supress_context = ( + hasattr(exc_value, "__suppress_context__") and exc_value.__suppress_context__ # type: ignore + ) if should_supress_context: # Add direct cause. # The field `__cause__` is set when raised with the exception (using the `from` keyword). @@ -1845,7 +1848,6 @@ def now(): from gevent import get_hub as get_gevent_hub from gevent.monkey import is_module_patched except ImportError: - # it's not great that the signatures are different, get_hub can't return None # consider adding an if TYPE_CHECKING to change the signature to Optional[Hub] def get_gevent_hub(): # type: ignore[misc] diff --git a/tests/conftest.py b/tests/conftest.py index 01b1e9a81f..faa0251d9b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -384,7 +384,6 @@ def render_span(span): root_span = event["contexts"]["trace"] - # Return a list instead of a multiline string because black will know better how to format that return "\n".join(render_span(root_span)) return inner diff --git a/tests/integrations/asyncio/test_asyncio.py b/tests/integrations/asyncio/test_asyncio.py index fb75bfc69b..66113746bf 100644 --- a/tests/integrations/asyncio/test_asyncio.py +++ b/tests/integrations/asyncio/test_asyncio.py @@ -291,7 +291,8 @@ def test_sentry_task_factory_with_factory(mock_get_running_loop): @patch("asyncio.get_running_loop") @patch("sentry_sdk.integrations.asyncio.Task") def test_sentry_task_factory_context_no_factory( - MockTask, mock_get_running_loop # noqa: N803 + MockTask, + mock_get_running_loop, # noqa: N803 ): mock_loop = mock_get_running_loop.return_value mock_coro = MagicMock() diff --git a/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/RaiseErrorPerformanceDisabled/.gitignore b/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/RaiseErrorPerformanceDisabled/.gitignore index ee0b7b9305..1c56884372 100644 --- a/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/RaiseErrorPerformanceDisabled/.gitignore +++ b/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/RaiseErrorPerformanceDisabled/.gitignore @@ -1,4 +1,4 @@ -# Need to add some ignore rules in this directory, because the unit tests will add the Sentry SDK and its dependencies +# Need to add some ignore rules in this directory, because the unit tests will add the Sentry SDK and its dependencies # into this directory to create a Lambda function package that contains everything needed to instrument a Lambda function using Sentry. # Ignore everything @@ -8,4 +8,4 @@ !index.py # And not .gitignore itself -!.gitignore \ No newline at end of file +!.gitignore diff --git a/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/RaiseErrorPerformanceEnabled/.gitignore b/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/RaiseErrorPerformanceEnabled/.gitignore index ee0b7b9305..1c56884372 100644 --- a/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/RaiseErrorPerformanceEnabled/.gitignore +++ b/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/RaiseErrorPerformanceEnabled/.gitignore @@ -1,4 +1,4 @@ -# Need to add some ignore rules in this directory, because the unit tests will add the Sentry SDK and its dependencies +# Need to add some ignore rules in this directory, because the unit tests will add the Sentry SDK and its dependencies # into this directory to create a Lambda function package that contains everything needed to instrument a Lambda function using Sentry. # Ignore everything @@ -8,4 +8,4 @@ !index.py # And not .gitignore itself -!.gitignore \ No newline at end of file +!.gitignore diff --git a/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/TracesSampler/.gitignore b/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/TracesSampler/.gitignore index ee0b7b9305..1c56884372 100644 --- a/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/TracesSampler/.gitignore +++ b/tests/integrations/aws_lambda/lambda_functions_with_embedded_sdk/TracesSampler/.gitignore @@ -1,4 +1,4 @@ -# Need to add some ignore rules in this directory, because the unit tests will add the Sentry SDK and its dependencies +# Need to add some ignore rules in this directory, because the unit tests will add the Sentry SDK and its dependencies # into this directory to create a Lambda function package that contains everything needed to instrument a Lambda function using Sentry. # Ignore everything @@ -8,4 +8,4 @@ !index.py # And not .gitignore itself -!.gitignore \ No newline at end of file +!.gitignore diff --git a/tests/integrations/beam/test_beam.py b/tests/integrations/beam/test_beam.py index 8c503b4c8c..809c4122e4 100644 --- a/tests/integrations/beam/test_beam.py +++ b/tests/integrations/beam/test_beam.py @@ -144,10 +144,10 @@ def test_monkey_patch_signature(f, args, kwargs): try: expected_signature = inspect.signature(f) test_signature = inspect.signature(f_temp) - assert ( - expected_signature == test_signature - ), "Failed on {}, signature {} does not match {}".format( - f, expected_signature, test_signature + assert expected_signature == test_signature, ( + "Failed on {}, signature {} does not match {}".format( + f, expected_signature, test_signature + ) ) except Exception: # expected to pass for py2.7 diff --git a/tests/integrations/celery/test_celery.py b/tests/integrations/celery/test_celery.py index 80b4a423cb..b8fc2bb3e8 100644 --- a/tests/integrations/celery/test_celery.py +++ b/tests/integrations/celery/test_celery.py @@ -374,9 +374,9 @@ def dummy_task(self): assert submit_transaction["type"] == "transaction" assert submit_transaction["transaction"] == "submit_celery" - assert len( - submit_transaction["spans"] - ), 4 # Because redis integration was auto enabled + assert len(submit_transaction["spans"]), ( + 4 + ) # Because redis integration was auto enabled span = submit_transaction["spans"][0] assert span["op"] == "queue.submit.celery" assert span["description"] == "dummy_task" @@ -439,7 +439,9 @@ def dummy_task(self, x, y): def test_traces_sampler_gets_task_info_in_sampling_context( - init_celery, celery_invocation, DictionaryContaining # noqa:N803 + init_celery, + celery_invocation, + DictionaryContaining, # noqa:N803 ): traces_sampler = mock.Mock() celery = init_celery(traces_sampler=traces_sampler) diff --git a/tests/integrations/django/asgi/test_asgi.py b/tests/integrations/django/asgi/test_asgi.py index 3c78ac3f38..8a30c7f5c0 100644 --- a/tests/integrations/django/asgi/test_asgi.py +++ b/tests/integrations/django/asgi/test_asgi.py @@ -432,9 +432,7 @@ async def test_trace_from_headers_if_performance_disabled(sentry_init, capture_e PICTURE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "image.png") BODY_FORM = """--fd721ef49ea403a6\r\nContent-Disposition: form-data; name="username"\r\n\r\nJane\r\n--fd721ef49ea403a6\r\nContent-Disposition: form-data; name="password"\r\n\r\nhello123\r\n--fd721ef49ea403a6\r\nContent-Disposition: form-data; name="photo"; filename="image.png"\r\nContent-Type: image/png\r\nContent-Transfer-Encoding: base64\r\n\r\n{{image_data}}\r\n--fd721ef49ea403a6--\r\n""".replace( "{{image_data}}", base64.b64encode(open(PICTURE, "rb").read()).decode("utf-8") -).encode( - "utf-8" -) +).encode("utf-8") BODY_FORM_CONTENT_LENGTH = str(len(BODY_FORM)).encode("utf-8") diff --git a/tests/integrations/django/test_cache_module.py b/tests/integrations/django/test_cache_module.py index 263f9f36f8..bc58dd8471 100644 --- a/tests/integrations/django/test_cache_module.py +++ b/tests/integrations/django/test_cache_module.py @@ -529,33 +529,33 @@ def test_cache_spans_get_many(sentry_init, capture_events, use_django_caching): from django.core.cache import cache with sentry_sdk.start_transaction(): - cache.get_many([f"S{id}", f"S{id+1}"]) + cache.get_many([f"S{id}", f"S{id + 1}"]) cache.set(f"S{id}", "Sensitive1") - cache.get_many([f"S{id}", f"S{id+1}"]) + cache.get_many([f"S{id}", f"S{id + 1}"]) (transaction,) = events assert len(transaction["spans"]) == 7 assert transaction["spans"][0]["op"] == "cache.get" - assert transaction["spans"][0]["description"] == f"S{id}, S{id+1}" + assert transaction["spans"][0]["description"] == f"S{id}, S{id + 1}" assert transaction["spans"][1]["op"] == "cache.get" assert transaction["spans"][1]["description"] == f"S{id}" assert transaction["spans"][2]["op"] == "cache.get" - assert transaction["spans"][2]["description"] == f"S{id+1}" + assert transaction["spans"][2]["description"] == f"S{id + 1}" assert transaction["spans"][3]["op"] == "cache.put" assert transaction["spans"][3]["description"] == f"S{id}" assert transaction["spans"][4]["op"] == "cache.get" - assert transaction["spans"][4]["description"] == f"S{id}, S{id+1}" + assert transaction["spans"][4]["description"] == f"S{id}, S{id + 1}" assert transaction["spans"][5]["op"] == "cache.get" assert transaction["spans"][5]["description"] == f"S{id}" assert transaction["spans"][6]["op"] == "cache.get" - assert transaction["spans"][6]["description"] == f"S{id+1}" + assert transaction["spans"][6]["description"] == f"S{id + 1}" @pytest.mark.forked @@ -578,20 +578,20 @@ def test_cache_spans_set_many(sentry_init, capture_events, use_django_caching): from django.core.cache import cache with sentry_sdk.start_transaction(): - cache.set_many({f"S{id}": "Sensitive1", f"S{id+1}": "Sensitive2"}) + cache.set_many({f"S{id}": "Sensitive1", f"S{id + 1}": "Sensitive2"}) cache.get(f"S{id}") (transaction,) = events assert len(transaction["spans"]) == 4 assert transaction["spans"][0]["op"] == "cache.put" - assert transaction["spans"][0]["description"] == f"S{id}, S{id+1}" + assert transaction["spans"][0]["description"] == f"S{id}, S{id + 1}" assert transaction["spans"][1]["op"] == "cache.put" assert transaction["spans"][1]["description"] == f"S{id}" assert transaction["spans"][2]["op"] == "cache.put" - assert transaction["spans"][2]["description"] == f"S{id+1}" + assert transaction["spans"][2]["description"] == f"S{id + 1}" assert transaction["spans"][3]["op"] == "cache.get" assert transaction["spans"][3]["description"] == f"S{id}" diff --git a/tests/integrations/excepthook/test_excepthook.py b/tests/integrations/excepthook/test_excepthook.py index 745f62d818..5a19b4f985 100644 --- a/tests/integrations/excepthook/test_excepthook.py +++ b/tests/integrations/excepthook/test_excepthook.py @@ -32,9 +32,7 @@ def capture_envelope(self, envelope): frame_value = "LOL" 1/0 - """.format( - transport=transport, options=options - ) + """.format(transport=transport, options=options) ) ) @@ -75,9 +73,7 @@ def capture_envelope(self, envelope): frame_value = "LOL" 1/0 - """.format( - transport=transport, options=options - ) + """.format(transport=transport, options=options) ) ) diff --git a/tests/integrations/fastapi/test_fastapi.py b/tests/integrations/fastapi/test_fastapi.py index 3d79da92cc..a69978ded4 100644 --- a/tests/integrations/fastapi/test_fastapi.py +++ b/tests/integrations/fastapi/test_fastapi.py @@ -271,9 +271,9 @@ def test_response_status_code_ok_in_transaction_context(sentry_init, capture_env assert transaction["type"] == "transaction" assert len(transaction["contexts"]) > 0 - assert ( - "response" in transaction["contexts"].keys() - ), "Response context not found in transaction" + assert "response" in transaction["contexts"].keys(), ( + "Response context not found in transaction" + ) assert transaction["contexts"]["response"]["status_code"] == 200 @@ -307,9 +307,9 @@ def test_response_status_code_error_in_transaction_context( assert transaction["type"] == "transaction" assert len(transaction["contexts"]) > 0 - assert ( - "response" in transaction["contexts"].keys() - ), "Response context not found in transaction" + assert "response" in transaction["contexts"].keys(), ( + "Response context not found in transaction" + ) assert transaction["contexts"]["response"]["status_code"] == 500 @@ -338,9 +338,9 @@ def test_response_status_code_not_found_in_transaction_context( assert transaction["type"] == "transaction" assert len(transaction["contexts"]) > 0 - assert ( - "response" in transaction["contexts"].keys() - ), "Response context not found in transaction" + assert "response" in transaction["contexts"].keys(), ( + "Response context not found in transaction" + ) assert transaction["contexts"]["response"]["status_code"] == 404 diff --git a/tests/integrations/flask/test_flask.py b/tests/integrations/flask/test_flask.py index 49ee684797..e117b98ca9 100644 --- a/tests/integrations/flask/test_flask.py +++ b/tests/integrations/flask/test_flask.py @@ -943,9 +943,9 @@ def test_response_status_code_ok_in_transaction_context( assert transaction["type"] == "transaction" assert len(transaction["contexts"]) > 0 - assert ( - "response" in transaction["contexts"].keys() - ), "Response context not found in transaction" + assert "response" in transaction["contexts"].keys(), ( + "Response context not found in transaction" + ) assert transaction["contexts"]["response"]["status_code"] == 200 @@ -970,9 +970,9 @@ def test_response_status_code_not_found_in_transaction_context( assert transaction["type"] == "transaction" assert len(transaction["contexts"]) > 0 - assert ( - "response" in transaction["contexts"].keys() - ), "Response context not found in transaction" + assert "response" in transaction["contexts"].keys(), ( + "Response context not found in transaction" + ) assert transaction["contexts"]["response"]["status_code"] == 404 diff --git a/tests/integrations/gcp/test_gcp.py b/tests/integrations/gcp/test_gcp.py index 22d104c817..d088f134fe 100644 --- a/tests/integrations/gcp/test_gcp.py +++ b/tests/integrations/gcp/test_gcp.py @@ -101,14 +101,14 @@ def inner(code, subprocess_kwargs=()): subprocess.check_call( [sys.executable, "setup.py", "sdist", "-d", os.path.join(tmpdir, "..")], - **subprocess_kwargs + **subprocess_kwargs, ) subprocess.check_call( "pip install ../*.tar.gz -t .", cwd=tmpdir, shell=True, - **subprocess_kwargs + **subprocess_kwargs, ) stream = os.popen("python {}/main.py".format(tmpdir)) @@ -280,7 +280,8 @@ def cloud_function(functionhandler, event): def test_traces_sampler_gets_correct_values_in_sampling_context( - run_cloud_function, DictionaryContaining # noqa:N803 + run_cloud_function, + DictionaryContaining, # noqa:N803 ): # TODO: There are some decent sized hacks below. For more context, see the # long comment in the test of the same name in the AWS integration. The diff --git a/tests/integrations/gql/test_gql.py b/tests/integrations/gql/test_gql.py index f87fb974d0..147f7a06a8 100644 --- a/tests/integrations/gql/test_gql.py +++ b/tests/integrations/gql/test_gql.py @@ -44,16 +44,16 @@ def _make_erroneous_query(capture_events): with pytest.raises(TransportQueryError): _execute_mock_query(response_json) - assert ( - len(events) == 1 - ), "the sdk captured %d events, but 1 event was expected" % len(events) + assert len(events) == 1, ( + "the sdk captured %d events, but 1 event was expected" % len(events) + ) (event,) = events (exception,) = event["exception"]["values"] - assert ( - exception["type"] == "TransportQueryError" - ), "%s was captured, but we expected a TransportQueryError" % exception(type) + assert exception["type"] == "TransportQueryError", ( + "%s was captured, but we expected a TransportQueryError" % exception(type) + ) assert "request" in event @@ -79,12 +79,12 @@ def test_real_gql_request_no_error(sentry_init, capture_events): result = _execute_mock_query(response_json) - assert ( - result == response_data - ), "client.execute returned a different value from what it received from the server" - assert ( - len(events) == 0 - ), "the sdk captured an event, even though the query was successful" + assert result == response_data, ( + "client.execute returned a different value from what it received from the server" + ) + assert len(events) == 0, ( + "the sdk captured an event, even though the query was successful" + ) def test_real_gql_request_with_error_no_pii(sentry_init, capture_events): diff --git a/tests/integrations/httpx/test_httpx.py b/tests/integrations/httpx/test_httpx.py index ba2575ce59..4fd5275fb7 100644 --- a/tests/integrations/httpx/test_httpx.py +++ b/tests/integrations/httpx/test_httpx.py @@ -328,7 +328,8 @@ def test_option_trace_propagation_targets( integrations=[HttpxIntegration()], ) - with sentry_sdk.start_transaction(): # Must be in a transaction to propagate headers + # Must be in a transaction to propagate headers + with sentry_sdk.start_transaction(): if asyncio.iscoroutinefunction(httpx_client.get): asyncio.get_event_loop().run_until_complete(httpx_client.get(url)) else: diff --git a/tests/integrations/langchain/test_langchain.py b/tests/integrations/langchain/test_langchain.py index af4b6b8c56..ba49b2e508 100644 --- a/tests/integrations/langchain/test_langchain.py +++ b/tests/integrations/langchain/test_langchain.py @@ -223,9 +223,9 @@ def test_langchain_agent( assert "5" in chat_spans[1]["data"][SPANDATA.GEN_AI_RESPONSE_TEXT] # Verify tool calls are recorded when PII is enabled - assert SPANDATA.GEN_AI_RESPONSE_TOOL_CALLS in chat_spans[0].get( - "data", {} - ), "Tool calls should be recorded when send_default_pii=True and include_prompts=True" + assert SPANDATA.GEN_AI_RESPONSE_TOOL_CALLS in chat_spans[0].get("data", {}), ( + "Tool calls should be recorded when send_default_pii=True and include_prompts=True" + ) tool_calls_data = chat_spans[0]["data"][SPANDATA.GEN_AI_RESPONSE_TOOL_CALLS] assert isinstance(tool_calls_data, (list, str)) # Could be serialized if isinstance(tool_calls_data, str): @@ -261,9 +261,9 @@ def test_langchain_agent( span_data = chat_span.get("data", {}) if SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS in span_data: tools_data = span_data[SPANDATA.GEN_AI_REQUEST_AVAILABLE_TOOLS] - assert ( - tools_data is not None - ), "Available tools should always be recorded regardless of PII settings" + assert tools_data is not None, ( + "Available tools should always be recorded regardless of PII settings" + ) def test_langchain_error(sentry_init, capture_events): @@ -537,9 +537,9 @@ def test_span_map_is_instance_variable(): callback2 = SentryLangchainCallback(max_span_map_size=100, include_prompts=True) # Verify they have different span_map instances - assert ( - callback1.span_map is not callback2.span_map - ), "span_map should be an instance variable, not shared between instances" + assert callback1.span_map is not callback2.span_map, ( + "span_map should be an instance variable, not shared between instances" + ) def test_langchain_callback_manager(sentry_init): diff --git a/tests/integrations/langgraph/test_langgraph.py b/tests/integrations/langgraph/test_langgraph.py index 5e35f772f5..1510305b06 100644 --- a/tests/integrations/langgraph/test_langgraph.py +++ b/tests/integrations/langgraph/test_langgraph.py @@ -333,7 +333,6 @@ async def original_ainvoke(self, *args, **kwargs): async def run_test(): with start_transaction(): - wrapped_ainvoke = _wrap_pregel_ainvoke(original_ainvoke) result = await wrapped_ainvoke(pregel, test_state) return result @@ -394,7 +393,6 @@ def original_invoke(self, *args, **kwargs): raise Exception("Graph execution failed") with start_transaction(), pytest.raises(Exception, match="Graph execution failed"): - wrapped_invoke = _wrap_pregel_invoke(original_invoke) wrapped_invoke(pregel, test_state) @@ -426,7 +424,6 @@ async def run_error_test(): with start_transaction(), pytest.raises( Exception, match="Async graph execution failed" ): - wrapped_ainvoke = _wrap_pregel_ainvoke(original_ainvoke) await wrapped_ainvoke(pregel, test_state) @@ -482,7 +479,6 @@ def test_pregel_invoke_with_different_graph_names( pregel = MockPregelInstance(graph_name) if graph_name else MockPregelInstance() if not graph_name: - delattr(pregel, "name") delattr(pregel, "graph_name") @@ -490,7 +486,6 @@ def original_invoke(self, *args, **kwargs): return {"result": "test"} with start_transaction(): - wrapped_invoke = _wrap_pregel_invoke(original_invoke) wrapped_invoke(pregel, {"messages": []}) diff --git a/tests/integrations/opentelemetry/test_span_processor.py b/tests/integrations/opentelemetry/test_span_processor.py index ec5cf6af23..cbee14f4d6 100644 --- a/tests/integrations/opentelemetry/test_span_processor.py +++ b/tests/integrations/opentelemetry/test_span_processor.py @@ -549,8 +549,10 @@ def test_pruning_old_spans_on_start(): current_time_minutes = int(time.time() / 60) span_processor.open_spans = { current_time_minutes - 3: {"111111111abcdef"}, # should stay - current_time_minutes - - 11: {"2222222222abcdef", "3333333333abcdef"}, # should go + current_time_minutes - 11: { + "2222222222abcdef", + "3333333333abcdef", + }, # should go } span_processor.on_start(otel_span, parent_context) @@ -599,8 +601,10 @@ def test_pruning_old_spans_on_end(): span_processor.open_spans = { current_time_minutes: {"1234567890abcdef"}, # should go (because it is closed) current_time_minutes - 3: {"111111111abcdef"}, # should stay - current_time_minutes - - 11: {"2222222222abcdef", "3333333333abcdef"}, # should go + current_time_minutes - 11: { + "2222222222abcdef", + "3333333333abcdef", + }, # should go } span_processor.on_end(otel_span) diff --git a/tests/integrations/rq/test_rq.py b/tests/integrations/rq/test_rq.py index e445b588be..23603ad91d 100644 --- a/tests/integrations/rq/test_rq.py +++ b/tests/integrations/rq/test_rq.py @@ -97,7 +97,9 @@ def test_transport_shutdown(sentry_init, capture_events_forksafe): def test_transaction_with_error( - sentry_init, capture_events, DictionaryContaining # noqa:N803 + sentry_init, + capture_events, + DictionaryContaining, # noqa:N803 ): sentry_init(integrations=[RqIntegration()], traces_sample_rate=1.0) events = capture_events() @@ -195,7 +197,9 @@ def test_tracing_disabled( def test_transaction_no_error( - sentry_init, capture_events, DictionaryContaining # noqa:N803 + sentry_init, + capture_events, + DictionaryContaining, # noqa:N803 ): sentry_init(integrations=[RqIntegration()], traces_sample_rate=1.0) events = capture_events() @@ -222,7 +226,9 @@ def test_transaction_no_error( def test_traces_sampler_gets_correct_values_in_sampling_context( - sentry_init, DictionaryContaining, ObjectDescribedBy # noqa:N803 + sentry_init, + DictionaryContaining, + ObjectDescribedBy, # noqa:N803 ): traces_sampler = mock.Mock(return_value=True) sentry_init(integrations=[RqIntegration()], traces_sampler=traces_sampler) diff --git a/tests/integrations/spark/test_spark.py b/tests/integrations/spark/test_spark.py index 0ea2770b89..c5bb70f4d1 100644 --- a/tests/integrations/spark/test_spark.py +++ b/tests/integrations/spark/test_spark.py @@ -90,7 +90,6 @@ def test_initialize_spark_integration_after_spark_context_init( @pytest.fixture def sentry_listener(): - listener = SentryListener() return listener diff --git a/tests/integrations/sys_exit/test_sys_exit.py b/tests/integrations/sys_exit/test_sys_exit.py index 81a950c7c0..a9909ae3c2 100644 --- a/tests/integrations/sys_exit/test_sys_exit.py +++ b/tests/integrations/sys_exit/test_sys_exit.py @@ -66,6 +66,6 @@ def test_sys_exit_integration_not_auto_enabled(sentry_init, capture_events): "sys.exit should not be patched, but it must have been because it did not raise SystemExit" ) - assert ( - len(events) == 0 - ), "No events should have been captured because sys.exit should not have been patched" + assert len(events) == 0, ( + "No events should have been captured because sys.exit should not have been patched" + ) diff --git a/tests/integrations/threading/test_threading.py b/tests/integrations/threading/test_threading.py index 4577c846d8..799298910b 100644 --- a/tests/integrations/threading/test_threading.py +++ b/tests/integrations/threading/test_threading.py @@ -208,9 +208,9 @@ def do_some_work(): t.join() # check if the initial scope data is not modified by the started thread - assert initial_iso_scope._tags == { - "initial_tag": "initial_value" - }, "The isolation scope in the main thread should not be modified by the started thread." + assert initial_iso_scope._tags == {"initial_tag": "initial_value"}, ( + "The isolation scope in the main thread should not be modified by the started thread." + ) @pytest.mark.parametrize( diff --git a/tests/integrations/unleash/testutils.py b/tests/integrations/unleash/testutils.py index 07b065e2f0..4e91b6190b 100644 --- a/tests/integrations/unleash/testutils.py +++ b/tests/integrations/unleash/testutils.py @@ -34,7 +34,6 @@ def mock_unleash_client(): class MockUnleashClient: - def __init__(self, *a, **kw): self.features = { "hello": True, diff --git a/tests/integrations/unraisablehook/test_unraisablehook.py b/tests/integrations/unraisablehook/test_unraisablehook.py index 2f97886ce8..dbe8164cf5 100644 --- a/tests/integrations/unraisablehook/test_unraisablehook.py +++ b/tests/integrations/unraisablehook/test_unraisablehook.py @@ -42,9 +42,7 @@ def capture_envelope(self, envelope): undeletable = Undeletable() del undeletable - """.format( - transport=transport, options=options - ) + """.format(transport=transport, options=options) ) ) diff --git a/tests/integrations/wsgi/test_wsgi.py b/tests/integrations/wsgi/test_wsgi.py index 656fc1757f..a741d1c57b 100644 --- a/tests/integrations/wsgi/test_wsgi.py +++ b/tests/integrations/wsgi/test_wsgi.py @@ -136,7 +136,10 @@ def test_keyboard_interrupt_is_captured(sentry_init, capture_events): def test_transaction_with_error( - sentry_init, crashing_app, capture_events, DictionaryContaining # noqa:N803 + sentry_init, + crashing_app, + capture_events, + DictionaryContaining, # noqa:N803 ): def dogpark(environ, start_response): raise ValueError("Fetch aborted. The ball was not returned.") @@ -173,7 +176,9 @@ def dogpark(environ, start_response): def test_transaction_no_error( - sentry_init, capture_events, DictionaryContaining # noqa:N803 + sentry_init, + capture_events, + DictionaryContaining, # noqa:N803 ): def dogpark(environ, start_response): start_response("200 OK", []) diff --git a/tests/test_basics.py b/tests/test_basics.py index 45303c9a59..b0b577b796 100644 --- a/tests/test_basics.py +++ b/tests/test_basics.py @@ -1218,6 +1218,6 @@ def recurse(): # On my machine, it takes about 100-200ms to capture the exception, # so this limit should be generous enough. - assert ( - capture_end_time - capture_start_time < 10**9 * 2 - ), "stacktrace capture took too long, check that frame limit is set correctly" + assert capture_end_time - capture_start_time < 10**9 * 2, ( + "stacktrace capture took too long, check that frame limit is set correctly" + ) diff --git a/tests/test_client.py b/tests/test_client.py index a02ea6e56a..ff3f61d702 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -608,9 +608,7 @@ def capture_envelope(self, envelope): for _ in range({num_messages}): capture_message("HI") - """.format( - transport=transport, options=options, num_messages=num_messages - ) + """.format(transport=transport, options=options, num_messages=num_messages) ) ) @@ -1194,9 +1192,9 @@ def test_spotlight_option( client = sentry_sdk.get_client() url = client.spotlight.url if client.spotlight else None - assert ( - url == spotlight_url_expected - ), f"With config {client_option} and env {env_var_value}" + assert url == spotlight_url_expected, ( + f"With config {client_option} and env {env_var_value}" + ) class IssuesSamplerTestConfig: diff --git a/tests/test_conftest.py b/tests/test_conftest.py index 3b8cd098f5..a36fe17894 100644 --- a/tests/test_conftest.py +++ b/tests/test_conftest.py @@ -22,7 +22,9 @@ ], ) def test_string_containing( - test_string, expected_result, StringContaining # noqa: N803 + test_string, + expected_result, + StringContaining, # noqa: N803 ): assert (test_string == StringContaining("dogs")) is expected_result @@ -46,7 +48,9 @@ def test_string_containing( ], ) def test_dictionary_containing( - test_dict, expected_result, DictionaryContaining # noqa: N803 + test_dict, + expected_result, + DictionaryContaining, # noqa: N803 ): assert ( test_dict == DictionaryContaining({"dogs": "yes", "cats": "maybe"}) diff --git a/tests/test_gevent.py b/tests/test_gevent.py index d330760adf..05fa6ed2e8 100644 --- a/tests/test_gevent.py +++ b/tests/test_gevent.py @@ -104,13 +104,11 @@ def test_transport_works_gevent( (compression_level is None) or ( # setting compression level to 0 means don't compress - compression_level - > 0 + compression_level > 0 ) ) and ( # if we couldn't resolve to a known algo, we don't compress - compression_algo - != "" + compression_algo != "" ) assert capturing_server.captured[0].compressed == should_compress diff --git a/tests/test_propagationcontext.py b/tests/test_propagationcontext.py index 078a69c72b..e014012956 100644 --- a/tests/test_propagationcontext.py +++ b/tests/test_propagationcontext.py @@ -155,8 +155,7 @@ def mock_random_class(seed): ) assert ( - ctx.dynamic_sampling_context["sample_rand"] - == f"{expected_interval[0]:.6f}" # noqa: E231 + ctx.dynamic_sampling_context["sample_rand"] == f"{expected_interval[0]:.6f}" # noqa: E231 ) assert mock_randrange.call_count == 1 assert mock_randrange.call_args[0] == ( diff --git a/tests/test_transport.py b/tests/test_transport.py index e493515e9a..68669fa24d 100644 --- a/tests/test_transport.py +++ b/tests/test_transport.py @@ -137,13 +137,11 @@ def test_transport_works( (compression_level is None) or ( # setting compression level to 0 means don't compress - compression_level - > 0 + compression_level > 0 ) ) and ( # if we couldn't resolve to a known algo, we don't compress - compression_algo - != "" + compression_algo != "" ) assert capturing_server.captured[0].compressed == should_compress diff --git a/tests/test_utils.py b/tests/test_utils.py index b268fbd57b..e1c6786e1b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -196,9 +196,9 @@ def test_datetime_from_isoformat_with_py_36_or_lower(input_str, expected_output) ], ) def test_env_to_bool(env_var_value, strict, expected): - assert ( - env_to_bool(env_var_value, strict=strict) == expected - ), f"Value: {env_var_value}, strict: {strict}" + assert env_to_bool(env_var_value, strict=strict) == expected, ( + f"Value: {env_var_value}, strict: {strict}" + ) @pytest.mark.parametrize( diff --git a/tests/tracing/test_sampling.py b/tests/tracing/test_sampling.py index 1761a3dbac..63da5a1399 100644 --- a/tests/tracing/test_sampling.py +++ b/tests/tracing/test_sampling.py @@ -212,7 +212,8 @@ def mock_set_initial_sampling_decision(_, sampling_context): def test_passes_custom_sampling_context_from_start_transaction_to_traces_sampler( - sentry_init, DictionaryContaining # noqa: N803 + sentry_init, + DictionaryContaining, # noqa: N803 ): traces_sampler = mock.Mock() sentry_init(traces_sampler=traces_sampler) @@ -251,7 +252,9 @@ def test_sample_rate_affects_errors(sentry_init, capture_events): ], ) def test_warns_and_sets_sampled_to_false_on_invalid_traces_sampler_return_value( - sentry_init, traces_sampler_return_value, StringContaining # noqa: N803 + sentry_init, + traces_sampler_return_value, + StringContaining, # noqa: N803 ): sentry_init(traces_sampler=mock.Mock(return_value=traces_sampler_return_value)) diff --git a/tox.ini b/tox.ini index b993397389..e310a6245b 100644 --- a/tox.ini +++ b/tox.ini @@ -91,7 +91,7 @@ envlist = {py3.6,py3.7}-boto3-v1.12.49 {py3.6,py3.9,py3.10}-boto3-v1.20.54 {py3.7,py3.11,py3.12}-boto3-v1.28.85 - {py3.9,py3.12,py3.13}-boto3-v1.40.38 + {py3.9,py3.12,py3.13}-boto3-v1.40.39 {py3.6,py3.7,py3.8}-chalice-v1.16.0 {py3.9,py3.12,py3.13}-chalice-v1.32.0 @@ -157,7 +157,7 @@ envlist = {py3.7,py3.8}-grpc-v1.32.0 {py3.7,py3.9,py3.10}-grpc-v1.47.5 {py3.7,py3.11,py3.12}-grpc-v1.62.3 - {py3.9,py3.12,py3.13}-grpc-v1.75.0 + {py3.9,py3.12,py3.13}-grpc-v1.75.1 {py3.6,py3.8,py3.9}-httpx-v0.16.1 {py3.6,py3.9,py3.10}-httpx-v0.20.0 @@ -396,7 +396,7 @@ deps = boto3-v1.12.49: boto3==1.12.49 boto3-v1.20.54: boto3==1.20.54 boto3-v1.28.85: boto3==1.28.85 - boto3-v1.40.38: boto3==1.40.38 + boto3-v1.40.39: boto3==1.40.39 {py3.7,py3.8}-boto3: urllib3<2.0.0 chalice-v1.16.0: chalice==1.16.0 @@ -482,7 +482,7 @@ deps = grpc-v1.32.0: grpcio==1.32.0 grpc-v1.47.5: grpcio==1.47.5 grpc-v1.62.3: grpcio==1.62.3 - grpc-v1.75.0: grpcio==1.75.0 + grpc-v1.75.1: grpcio==1.75.1 grpc: protobuf grpc: mypy-protobuf grpc: types-protobuf @@ -803,11 +803,9 @@ basepython = py3.12: python3.12 py3.13: python3.13 - # Python version is pinned here because flake8 actually behaves differently - # depending on which version is used. You can patch this out to point to - # some random Python 3 binary, but then you get guaranteed mismatches with - # CI. Other tools such as mypy and black have options that pin the Python - # version. + # Python version is pinned here for consistency across environments. + # Tools like ruff and mypy have options that pin the target Python + # version (configured in pyproject.toml), ensuring consistent behavior. linters: python3.12 commands = @@ -823,6 +821,6 @@ commands = [testenv:linters] commands = - flake8 tests sentry_sdk - black --check tests sentry_sdk + ruff check tests sentry_sdk + ruff format --check tests sentry_sdk mypy sentry_sdk