Add type checker (#3116)

This commit is contained in:
Marcelo Trylesinski 2025-01-13 17:20:33 +00:00 committed by GitHub
parent 8406e2e789
commit 406707b2bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 82 additions and 11 deletions

View File

@ -152,3 +152,21 @@ jobs:
- name: Run tests - name: Run tests
run: tox -e ruff run: tox -e ruff
typecheck:
name: typecheck
runs-on: ubuntu-latest
steps:
- name: Checkout repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4
- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install tox
run: pip install tox
- name: Run tests
run: tox -e typecheck

View File

@ -1,6 +1,6 @@
pylint==3.0.2 pylint==3.0.2
httpretty==1.1.4 httpretty==1.1.4
mypy==0.931 pyright==v1.1.390
sphinx==7.1.2 sphinx==7.1.2
sphinx-rtd-theme==2.0.0rc4 sphinx-rtd-theme==2.0.0rc4
sphinx-autodoc-typehints==1.25.2 sphinx-autodoc-typehints==1.25.2

View File

@ -35,17 +35,29 @@ context attached, and this context will be re-activated in the thread's
run method or the executor's worker thread." run method or the executor's worker thread."
""" """
from __future__ import annotations
import threading import threading
from concurrent import futures from concurrent import futures
from typing import Collection from typing import TYPE_CHECKING, Any, Callable, Collection
from wrapt import wrap_function_wrapper from wrapt import (
wrap_function_wrapper, # type: ignore[reportUnknownVariableType]
)
from opentelemetry import context from opentelemetry import context
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.instrumentation.threading.package import _instruments from opentelemetry.instrumentation.threading.package import _instruments
from opentelemetry.instrumentation.utils import unwrap from opentelemetry.instrumentation.utils import unwrap
if TYPE_CHECKING:
from typing import Protocol, TypeVar
R = TypeVar("R")
class HasOtelContext(Protocol):
_otel_context: context.Context
class ThreadingInstrumentor(BaseInstrumentor): class ThreadingInstrumentor(BaseInstrumentor):
__WRAPPER_START_METHOD = "start" __WRAPPER_START_METHOD = "start"
@ -55,12 +67,12 @@ class ThreadingInstrumentor(BaseInstrumentor):
def instrumentation_dependencies(self) -> Collection[str]: def instrumentation_dependencies(self) -> Collection[str]:
return _instruments return _instruments
def _instrument(self, **kwargs): def _instrument(self, **kwargs: Any):
self._instrument_thread() self._instrument_thread()
self._instrument_timer() self._instrument_timer()
self._instrument_thread_pool() self._instrument_thread_pool()
def _uninstrument(self, **kwargs): def _uninstrument(self, **kwargs: Any):
self._uninstrument_thread() self._uninstrument_thread()
self._uninstrument_timer() self._uninstrument_timer()
self._uninstrument_thread_pool() self._uninstrument_thread_pool()
@ -117,12 +129,22 @@ class ThreadingInstrumentor(BaseInstrumentor):
) )
@staticmethod @staticmethod
def __wrap_threading_start(call_wrapped, instance, args, kwargs): def __wrap_threading_start(
call_wrapped: Callable[[], None],
instance: HasOtelContext,
args: ...,
kwargs: ...,
) -> None:
instance._otel_context = context.get_current() instance._otel_context = context.get_current()
return call_wrapped(*args, **kwargs) return call_wrapped(*args, **kwargs)
@staticmethod @staticmethod
def __wrap_threading_run(call_wrapped, instance, args, kwargs): def __wrap_threading_run(
call_wrapped: Callable[..., R],
instance: HasOtelContext,
args: tuple[Any, ...],
kwargs: dict[str, Any],
) -> R:
token = None token = None
try: try:
token = context.attach(instance._otel_context) token = context.attach(instance._otel_context)
@ -131,12 +153,17 @@ class ThreadingInstrumentor(BaseInstrumentor):
context.detach(token) context.detach(token)
@staticmethod @staticmethod
def __wrap_thread_pool_submit(call_wrapped, instance, args, kwargs): def __wrap_thread_pool_submit(
call_wrapped: Callable[..., R],
instance: futures.ThreadPoolExecutor,
args: tuple[Callable[..., Any], ...],
kwargs: dict[str, Any],
) -> R:
# obtain the original function and wrapped kwargs # obtain the original function and wrapped kwargs
original_func = args[0] original_func = args[0]
otel_context = context.get_current() otel_context = context.get_current()
def wrapped_func(*func_args, **func_kwargs): def wrapped_func(*func_args: Any, **func_kwargs: Any) -> R:
token = None token = None
try: try:
token = context.attach(otel_context) token = context.attach(otel_context)
@ -145,5 +172,5 @@ class ThreadingInstrumentor(BaseInstrumentor):
context.detach(token) context.detach(token)
# replace the original function with the wrapped function # replace the original function with the wrapped function
new_args = (wrapped_func,) + args[1:] new_args: tuple[Callable[..., Any], ...] = (wrapped_func,) + args[1:]
return call_wrapped(*new_args, **kwargs) return call_wrapped(*new_args, **kwargs)

View File

@ -39,3 +39,19 @@ known-third-party = [
"opencensus", "opencensus",
] ]
# https://github.com/microsoft/pyright/blob/main/docs/configuration.md#type-check-rule-overrides
[tool.pyright]
typeCheckingMode = "strict"
reportUnnecessaryTypeIgnoreComment = true
reportMissingTypeStubs = false
pythonVersion = "3.8"
reportPrivateUsage = false # Ignore private attributes added by instrumentation packages.
# Add progressively instrumentation packages here.
include = [
"instrumentation/opentelemetry-instrumentation-threading/**/*.py"
]
# We should also add type hints to the test suite - It helps on finding bugs.
# We are excluding for now because it's easier, and more important to add to the instrumentation packages.
exclude = [
"instrumentation/opentelemetry-instrumentation-threading/tests/**",
]

12
tox.ini
View File

@ -404,6 +404,7 @@ envlist =
generate-workflows generate-workflows
shellcheck shellcheck
ruff ruff
typecheck
[testenv] [testenv]
test_deps = test_deps =
@ -677,7 +678,6 @@ deps =
util-http: -r {toxinidir}/util/opentelemetry-util-http/test-requirements.txt util-http: -r {toxinidir}/util/opentelemetry-util-http/test-requirements.txt
util-http: {toxinidir}/util/opentelemetry-util-http util-http: {toxinidir}/util/opentelemetry-util-http
; FIXME: add coverage testing ; FIXME: add coverage testing
; FIXME: add mypy testing
allowlist_externals = allowlist_externals =
sh sh
@ -986,3 +986,13 @@ deps =
pre-commit pre-commit
commands = commands =
pre-commit run --color=always --all-files {posargs} pre-commit run --color=always --all-files {posargs}
[testenv:typecheck]
deps =
-c {toxinidir}/dev-requirements.txt
pyright
{[testenv]test_deps}
{toxinidir}/opentelemetry-instrumentation
{toxinidir}/util/opentelemetry-util-http
commands =
pyright