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

MetrologyNamespace (initial PR) #9

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
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
68 changes: 68 additions & 0 deletions src/metrology_apis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,67 @@
__all__ = ["__version__", "Dimension", "Quantity", "Unit"]


@runtime_checkable
class MetrologyNamespace[Q: Quantity, U: Unit, D: Dimension](Protocol):

@staticmethod
def asdimension(obj: str | D) -> D: ...

@staticmethod
def asunit(obj) -> U[D]: ...
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def asunit(obj) -> U[D]: ...
def asunit(obj: str | U) -> U[D]: ...

should this be type hinted?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps #11 to consider


@staticmethod
def asquantity(obj: V, unit: obj) -> Q[V, U[D]]: ...

@property
def Dimension(self) -> D: ...

@property
def Unit(self) -> U: ...

@property
def Quantity(self) -> Q: ...
Comment on lines +23 to +30
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we even need to make the classes accessible?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. Like array api, which doesn't



@runtime_checkable
class Dimension(Protocol):
def __metrology_namespace__(
self, /, *, api_version: str | None = None
) -> MetrologyNamespace:
"""
Returns an object that has all the metrology API functions on it.
Parameters
----------
api_version: str or None
string representing the version of the metrology API specification to be returned. If it is ``None``, it should return the namespace corresponding to latest version of the metrology API specification. If the given version is invalid or not implemented for the given module, an error should be raised. Default: ``None``.
Returns
-------
out: Any
an object representing the metrology API namespace. It should have every top-level function defined in the specification as an attribute. It may contain other public names as well, but it is recommended to only include those names that are part of the specification.
"""

def __mul__(self, other: Self, /) -> Self: ...
def __truediv__(self, other: Self, /) -> Self: ...
def __pow__(self, other: int, /) -> Self: ...


@runtime_checkable
class Unit(Protocol):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we are suggesting here to parametrize Unit with a D: Dimension

Copy link

@nstarman nstarman Mar 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most libraries will have Dimension as only one class, but parametrizing like this allows for users/libraries to have different classes if they choose. Potential use cases include:

  1. Differentiating between base and composed dimensions (if they use different classes)
  2. Supporting different dimension classes, e.g greedy vs lazy resolution of the dimension
  3. Typing functions that can take a quantity from one library and convert it to a quantity of another library.

def __metrology_namespace__(
self, /, *, api_version: str | None = None
) -> MetrologyNamespace:
"""
Returns an object that has all the metrology API functions on it.
Parameters
----------
api_version: str or None
string representing the version of the metrology API specification to be returned. If it is ``None``, it should return the namespace corresponding to latest version of the metrology API specification. If the given version is invalid or not implemented for the given module, an error should be raised. Default: ``None``.
Returns
-------
out: Any
an object representing the metrology API namespace. It should have every top-level function defined in the specification as an attribute. It may contain other public names as well, but it is recommended to only include those names that are part of the specification.
"""

@property
def dimension(self) -> Dimension: ...

Expand All @@ -28,8 +80,24 @@ def __rmul__(self, other: Self, /) -> Self: ...
def __rtruediv__(self, other: Self, /) -> Self: ...
def __rpow__(self, other: int | float, /) -> Self: ...


@runtime_checkable
class Quantity[V, U: Unit](Protocol):
def __metrology_namespace__(
self, /, *, api_version: str | None = None
) -> MetrologyNamespace:
"""
Returns an object that has all the metrology API functions on it.
Parameters
----------
api_version: str or None
string representing the version of the metrology API specification to be returned. If it is ``None``, it should return the namespace corresponding to latest version of the metrology API specification. If the given version is invalid or not implemented for the given module, an error should be raised. Default: ``None``.
Returns
-------
out: Any
an object representing the metrology API namespace. It should have every top-level function defined in the specification as an attribute. It may contain other public names as well, but it is recommended to only include those names that are part of the specification.
"""

@property
def value(self) -> V: ...
@property
Expand Down