Skip to content

Commit

Permalink
Fix dataclass hierarchy uncovered by mypy (#734)
Browse files Browse the repository at this point in the history
* Fix dataclass hierarchy uncovered by mypy

* Remove __future__.annotations and other unrelated changes

* Revert missed change
  • Loading branch information
arr-ee committed Mar 26, 2023
1 parent df50969 commit aebdfe5
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 44 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ repos:
hooks:
- id: isort
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 23.1.0
hooks:
- id: black
- repo: https://github.com/pycqa/flake8
rev: 6.0.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.991 # Use the sha / tag you want to point at
rev: v1.1.1 # Use the sha / tag you want to point at
hooks:
- id: mypy
additional_dependencies: [numpy, typing_extensions]
89 changes: 47 additions & 42 deletions pyvisa/rname.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
:license: MIT, see LICENSE for more details.
"""

import contextlib
import re
from collections import OrderedDict, defaultdict
from dataclasses import dataclass, field, fields
from dataclasses import dataclass, fields
from typing import (
TYPE_CHECKING,
Callable,
Expand All @@ -22,7 +23,7 @@
TypeVar,
)

from typing_extensions import ClassVar
from typing_extensions import ClassVar, Self

from . import constants, errors, logger

Expand Down Expand Up @@ -51,7 +52,7 @@ def __init__(self, msg: str) -> None:
@classmethod
def bad_syntax(
cls, syntax: str, resource_name: str, ex: Optional[Exception] = None
) -> "InvalidResourceName":
) -> Self:
"""Build an exception when the resource name cannot be parsed."""
if ex:
msg = "The syntax is '%s' (%s)." % (syntax, ex)
Expand All @@ -67,7 +68,7 @@ def subclass_notfound(
cls,
interface_type_resource_class: Tuple[str, str],
resource_name: Optional[str] = None,
) -> "InvalidResourceName":
) -> Self:
"""Build an exception when no parser has been registered for a pair."""

msg = "Parser not found for: %s." % (interface_type_resource_class,)
Expand All @@ -80,7 +81,7 @@ def subclass_notfound(
@classmethod
def rc_notfound(
cls, interface_type: str, resource_name: Optional[str] = None
) -> "InvalidResourceName":
) -> Self:
"""Build an exception when no resource class is provided and no default is found."""

msg = (
Expand Down Expand Up @@ -108,7 +109,7 @@ def register_subclass(cls: T) -> T:
"""

# Assemble the format string based on the resource parts
fmt = OrderedDict([("interface_type", cls.interface_type)])
fmt: OrderedDict[str, str] = OrderedDict([("interface_type", cls.interface_type)])
syntax = cls.interface_type
for ndx, f in enumerate(fields(cls)):
sep = "::" if ndx else ""
Expand Down Expand Up @@ -148,46 +149,26 @@ def register_subclass(cls: T) -> T:
return cls


class ResourceName:
class _ResourceNameBase:
"""Base class for ResourceNames to be used as a mixin."""

#: Interface type string
interface_type: ClassVar[str]

#: Resource class string
resource_class: ClassVar[str]

#: Specifices if the resource class part of the string is optional.
is_rc_optional: ClassVar[bool] = False

#: Formatting string for canonical
_canonical_fmt: Dict[str, str] = field(init=False)
_canonical_fmt: Dict[str, str]

#: VISA syntax for resource
_visa_syntax: str = field(init=False)
_visa_syntax: str

#: VISA syntax for resource
_fields: Tuple[str, ...] = field(init=False)
_fields: Tuple[str, ...]

#: Resource name provided by the user (not empty only when parsing)
user: str = field(init=False)

def __post_init__(self):
# Ensure that all mandatory arguments have been passed
for f in fields(self):
if getattr(self, f.name) == "":
raise TypeError(f.name + " is a required parameter")
self._fields = tuple(f.name for f in fields(self))

@property
def interface_type_const(self) -> constants.InterfaceType:
try:
return getattr(constants.InterfaceType, self.interface_type.lower())
except Exception:
return constants.InterfaceType.unknown
user: Optional[str]

@classmethod
def from_string(cls, resource_name: str) -> "ResourceName":
def from_string(cls, resource_name: str) -> Self:
"""Parse a resource name and return a ResourceName
Parameters
Expand Down Expand Up @@ -252,7 +233,7 @@ def from_string(cls, resource_name: str) -> "ResourceName":
)

@classmethod
def from_kwargs(cls, **kwargs) -> "ResourceName":
def from_kwargs(cls, **kwargs) -> Self:
"""Build a resource from keyword arguments."""
interface_type = kwargs.pop("interface_type")

Expand All @@ -279,7 +260,39 @@ def from_kwargs(cls, **kwargs) -> "ResourceName":
except (ValueError, TypeError) as ex:
raise InvalidResourceName(str(ex))

# Implemented when building concrete subclass in build_rn_class
def __str__(self):
s = ""
for part, form in self._canonical_fmt.items():
value = getattr(self, part, None)
if value is not None:
s += form.format(value)
return s


@dataclass
class ResourceName(_ResourceNameBase):
#: Interface type string
interface_type: ClassVar[str]

#: Resource class string
resource_class: ClassVar[str]

def __post_init__(self):
# Ensure that all mandatory arguments have been passed
for f in fields(self):
if getattr(self, f.name) == "":
raise TypeError(f.name + " is a required parameter")
self._fields = tuple(f.name for f in fields(self))

@property
def interface_type_const(self) -> constants.InterfaceType:
try:
return getattr(constants.InterfaceType, self.interface_type.lower())
except Exception:
return constants.InterfaceType.unknown

# Implemented when building concrete subclass in build_rn_class

@classmethod
def from_parts(cls, *parts):
"""Construct a resource name from a list of parts."""
Expand Down Expand Up @@ -319,14 +332,6 @@ def from_parts(cls, *parts):

return cls(**kwargs)

def __str__(self):
s = ""
for part, form in self._canonical_fmt.items():
value = getattr(self, part, None)
if value is not None:
s += form.format(value)
return s


# Build subclasses for each resource

Expand Down

0 comments on commit aebdfe5

Please sign in to comment.