Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Running mypy on SDK resources (following up #3995) #4053

Merged
merged 35 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2522ace
This PR is based on #3995.
asasvari Jul 13, 2024
c81cb51
Update opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
asasvari Jul 14, 2024
d1d97f4
Update opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
asasvari Jul 14, 2024
12702cf
Update opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
asasvari Jul 14, 2024
26ad2c9
Update opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
asasvari Jul 14, 2024
4b1a16a
Update opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
asasvari Jul 14, 2024
33e35c2
address review comments
asasvari Jul 14, 2024
102328b
address review comments
asasvari Jul 14, 2024
d283fa5
Update opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
xrmx Jul 14, 2024
684d392
Merge branch 'main' into pr3995
asasvari Jul 19, 2024
f89d8a9
improve type hints based on review comments
asasvari Jul 19, 2024
b08ef70
black
asasvari Jul 19, 2024
9c0b119
flake8
asasvari Jul 19, 2024
3e36b69
Update opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
xrmx Jul 19, 2024
55ce61d
Adjust typehinting
asasvari Jul 19, 2024
16beda2
revert py3.10 style typehinting
asasvari Jul 19, 2024
127464b
Address review comments
asasvari Jul 22, 2024
d88c11b
Merge branch 'main' into pr3995
asasvari Jul 22, 2024
2d0fcc9
Update opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
asasvari Jul 23, 2024
071653a
fix mypy
asasvari Jul 23, 2024
70e2302
Merge branch 'main' into pr3995
emdneto Jul 23, 2024
c42e3e6
Update opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
asasvari Jul 23, 2024
fb47e31
fix mypy
asasvari Jul 23, 2024
bbbb7aa
fix linting issues
asasvari Jul 23, 2024
acf6ece
Merge branch 'main' into pr3995
xrmx Jul 24, 2024
d8575f1
Revert annotation as per review comment
asasvari Jul 24, 2024
779a707
use Tuple for tzpe hinting
asasvari Jul 25, 2024
9a3b3a4
Update CHANGELOG.md
asasvari Jul 25, 2024
090e4ba
revert type hinting in more files to fix mypy
asasvari Jul 25, 2024
0524a54
Merge branch 'main' into pr3995
asasvari Jul 25, 2024
3414c2c
Merge branch 'main' into pr3995
asasvari Jul 26, 2024
a917d31
Merge branch 'main' into pr3995
asasvari Jul 26, 2024
626ddd5
Merge branch 'main' into pr3995
asasvari Jul 30, 2024
d467cc8
Update CHANGELOG.md
xrmx Jul 30, 2024
aea5392
Merge branch 'main' into pr3995
lzchen Jul 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

- Added py.typed file to top-level module ([#4084](https://github.com/open-telemetry/opentelemetry-python/pull/4084))
- Running mypy on SDK resources
([#4053](https://github.com/open-telemetry/opentelemetry-python/pull/4053))

- Added py.typed file to top-level module
([#4084](https://github.com/open-telemetry/opentelemetry-python/pull/4084))

xrmx marked this conversation as resolved.
Show resolved Hide resolved
- Drop Final annotation from Enum in semantic conventions
([#4085](https://github.com/open-telemetry/opentelemetry-python/pull/4085))

Expand Down
47 changes: 25 additions & 22 deletions opentelemetry-api/src/opentelemetry/attributes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# type: ignore

import logging
import threading
from collections import OrderedDict
from collections.abc import MutableMapping
from typing import Optional, Sequence, Union
from typing import Optional, Sequence, Tuple, Union

from opentelemetry.util import types

Expand All @@ -31,7 +30,7 @@

def _clean_attribute(
key: str, value: types.AttributeValue, max_len: Optional[int]
) -> Optional[types.AttributeValue]:
) -> Optional[Union[types.AttributeValue, Tuple[Union[str, int, float], ...]]]:
"""Checks if attribute value is valid and cleans it if required.

The function returns the cleaned value or None if the value is not valid.
Expand Down Expand Up @@ -59,7 +58,7 @@ def _clean_attribute(
cleaned_seq = []

for element in value:
element = _clean_attribute_value(element, max_len)
element = _clean_attribute_value(element, max_len) # type: ignore
if element is None:
cleaned_seq.append(element)
continue
Expand Down Expand Up @@ -110,7 +109,7 @@ def _clean_attribute(

def _clean_attribute_value(
value: types.AttributeValue, limit: Optional[int]
) -> Union[types.AttributeValue, None]:
) -> Optional[types.AttributeValue]:
if value is None:
return None

Expand All @@ -126,7 +125,7 @@ def _clean_attribute_value(
return value


class BoundedAttributes(MutableMapping):
class BoundedAttributes(MutableMapping): # type: ignore
"""An ordered dict with a fixed max capacity.

Oldest elements are dropped when the dict is full and a new element is
Expand All @@ -149,28 +148,32 @@ def __init__(
self.dropped = 0
self.max_value_len = max_value_len
# OrderedDict is not used until the maxlen is reached for efficiency.
self._dict = {} # type: dict | OrderedDict
self._lock = threading.RLock() # type: threading.RLock

self._dict: Union[
MutableMapping[str, types.AttributeValue],
OrderedDict[str, types.AttributeValue],
] = {}
self._lock = threading.RLock()
if attributes:
for key, value in attributes.items():
self[key] = value
self._immutable = immutable

def __repr__(self):
def __repr__(self) -> str:
return f"{dict(self._dict)}"

def __getitem__(self, key):
def __getitem__(self, key: str) -> types.AttributeValue:
return self._dict[key]

def __setitem__(self, key, value):
if getattr(self, "_immutable", False):
def __setitem__(self, key: str, value: types.AttributeValue) -> None:
if getattr(self, "_immutable", False): # type: ignore
raise TypeError
with self._lock:
if self.maxlen is not None and self.maxlen == 0:
self.dropped += 1
return

value = _clean_attribute(key, value, self.max_value_len)
value = _clean_attribute(key, value, self.max_value_len) # type: ignore
if value is not None:
if key in self._dict:
del self._dict[key]
Expand All @@ -179,23 +182,23 @@ def __setitem__(self, key, value):
):
if not isinstance(self._dict, OrderedDict):
self._dict = OrderedDict(self._dict)
self._dict.popitem(last=False)
self._dict.popitem(last=False) # type: ignore
self.dropped += 1

self._dict[key] = value
self._dict[key] = value # type: ignore

def __delitem__(self, key):
if getattr(self, "_immutable", False):
def __delitem__(self, key: str) -> None:
if getattr(self, "_immutable", False): # type: ignore
raise TypeError
with self._lock:
del self._dict[key]

def __iter__(self):
def __iter__(self): # type: ignore
with self._lock:
return iter(self._dict.copy())
return iter(self._dict.copy()) # type: ignore

def __len__(self):
def __len__(self) -> int:
return len(self._dict)

def copy(self):
return self._dict.copy()
def copy(self): # type: ignore
xrmx marked this conversation as resolved.
Show resolved Hide resolved
return self._dict.copy() # type: ignore
55 changes: 34 additions & 21 deletions opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@
import typing
from json import dumps
from os import environ
from types import ModuleType
from typing import List, MutableMapping, Optional, cast
from urllib import parse

from opentelemetry.attributes import BoundedAttributes
Expand All @@ -75,10 +77,14 @@
from opentelemetry.util._importlib_metadata import entry_points, version
from opentelemetry.util.types import AttributeValue

psutil: Optional[ModuleType] = None

try:
import psutil
import psutil as psutil_module

pustil = psutil_module
except ImportError:
psutil = None
pass

LabelValue = AttributeValue
Attributes = typing.Mapping[str, LabelValue]
Expand Down Expand Up @@ -141,12 +147,15 @@
TELEMETRY_AUTO_VERSION = ResourceAttributes.TELEMETRY_AUTO_VERSION
TELEMETRY_SDK_LANGUAGE = ResourceAttributes.TELEMETRY_SDK_LANGUAGE

_OPENTELEMETRY_SDK_VERSION = version("opentelemetry-sdk")
_OPENTELEMETRY_SDK_VERSION: str = version("opentelemetry-sdk")


class Resource:
"""A Resource is an immutable representation of the entity producing telemetry as Attributes."""

_attributes: BoundedAttributes
_schema_url: str

def __init__(
self, attributes: Attributes, schema_url: typing.Optional[str] = None
):
Expand All @@ -173,7 +182,7 @@ def create(
if not attributes:
attributes = {}

resource_detectors = []
resource_detectors: List[ResourceDetector] = []

resource = _DEFAULT_RESOURCE

Expand All @@ -184,26 +193,27 @@ def create(
if "otel" not in otel_experimental_resource_detectors:
otel_experimental_resource_detectors.append("otel")

resource_detector: str
for resource_detector in otel_experimental_resource_detectors:
resource_detectors.append(
next(
iter(
entry_points(
group="opentelemetry_resource_detector",
name=resource_detector.strip(),
)
) # type: ignore
)
).load()()
)

resource = get_aggregated_resources(
resource_detectors, _DEFAULT_RESOURCE
).merge(Resource(attributes, schema_url))

if not resource.attributes.get(SERVICE_NAME, None):
default_service_name = "unknown_service"
process_executable_name = resource.attributes.get(
PROCESS_EXECUTABLE_NAME, None
process_executable_name = cast(
Optional[str],
resource.attributes.get(PROCESS_EXECUTABLE_NAME, None),
)
if process_executable_name:
default_service_name += ":" + process_executable_name
Expand Down Expand Up @@ -241,8 +251,8 @@ def merge(self, other: "Resource") -> "Resource":
Returns:
The newly-created Resource.
"""
merged_attributes = self.attributes.copy()
merged_attributes.update(other.attributes)
merged_attributes = self.attributes.copy() # type: ignore
merged_attributes.update(other.attributes) # type: ignore

if self.schema_url == "":
schema_url = other.schema_url
Expand All @@ -257,8 +267,7 @@ def merge(self, other: "Resource") -> "Resource":
other.schema_url,
)
return self
xrmx marked this conversation as resolved.
Show resolved Hide resolved

return Resource(merged_attributes, schema_url)
return Resource(merged_attributes, schema_url) # type: ignore

def __eq__(self, other: object) -> bool:
if not isinstance(other, Resource):
Expand All @@ -268,15 +277,18 @@ def __eq__(self, other: object) -> bool:
and self._schema_url == other._schema_url
)

def __hash__(self):
def __hash__(self) -> int:
return hash(
f"{dumps(self._attributes.copy(), sort_keys=True)}|{self._schema_url}"
f"{dumps(self._attributes.copy(), sort_keys=True)}|{self._schema_url}" # type: ignore
)

def to_json(self, indent=4) -> str:
def to_json(self, indent: int = 4) -> str:
attributes: MutableMapping[str, AttributeValue] = dict(
self._attributes
)
return dumps(
{
"attributes": dict(self._attributes),
"attributes": attributes, # type: ignore
"schema_url": self._schema_url,
},
indent=indent,
Expand All @@ -294,7 +306,7 @@ def to_json(self, indent=4) -> str:


class ResourceDetector(abc.ABC):
def __init__(self, raise_on_error=False):
def __init__(self, raise_on_error: bool = False) -> None:
self.raise_on_error = raise_on_error

@abc.abstractmethod
Expand Down Expand Up @@ -365,16 +377,17 @@ def detect(self) -> "Resource":
resource_info[PROCESS_PARENT_PID] = os.getppid()

if psutil is not None:
process = psutil.Process()
resource_info[PROCESS_OWNER] = process.username()
process: psutil_module.Process = psutil.Process()
username = process.username()
resource_info[PROCESS_OWNER] = username

return Resource(resource_info)
return Resource(resource_info) # type: ignore


def get_aggregated_resources(
detectors: typing.List["ResourceDetector"],
initial_resource: typing.Optional[Resource] = None,
timeout=5,
timeout: int = 5,
) -> "Resource":
"""Retrieves resources from detectors in the order that they were passed

Expand Down
4 changes: 3 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ setenv =
; i.e: CONTRIB_REPO_SHA=dde62cebffe519c35875af6d06fae053b3be65ec tox -e <env to test>
CONTRIB_REPO_SHA={env:CONTRIB_REPO_SHA:main}
CONTRIB_REPO=git+https://github.com/open-telemetry/opentelemetry-python-contrib.git@{env:CONTRIB_REPO_SHA}
mypy: MYPYPATH={toxinidir}/opentelemetry-api/src/:{toxinidir}/tests/opentelemetry-test-utils/src/
mypy: MYPYPATH={toxinidir}/opentelemetry-api/src/:{toxinidir}/opentelemetry-semantic-conventions/src/:{toxinidir}/opentelemetry-sdk/src/:{toxinidir}/tests/opentelemetry-test-utils/src/

commands_pre =

Expand Down Expand Up @@ -314,7 +314,9 @@ commands =

coverage: {toxinidir}/scripts/coverage.sh

mypy: mypy --version
mypy: mypy --install-types --non-interactive --namespace-packages --explicit-package-bases opentelemetry-api/src/opentelemetry/
mypy: mypy --install-types --non-interactive --namespace-packages --explicit-package-bases opentelemetry-sdk/src/opentelemetry/sdk/resources
mypy: mypy --install-types --non-interactive --namespace-packages --explicit-package-bases opentelemetry-semantic-conventions/src/opentelemetry/semconv/

; For test code, we don't want to enforce the full mypy strictness
Expand Down