diff --git a/openml/__init__.py b/openml/__init__.py index ae5db261f..909de2534 100644 --- a/openml/__init__.py +++ b/openml/__init__.py @@ -32,7 +32,8 @@ tasks, utils, ) -from .__version__ import __version__ + +__version__ = "0.16.0" # defined here directly, see issue #1642 from .datasets import OpenMLDataFeature, OpenMLDataset from .evaluations import OpenMLEvaluation from .flows import OpenMLFlow diff --git a/openml/__version__.py b/openml/__version__.py deleted file mode 100644 index cf5a8535d..000000000 --- a/openml/__version__.py +++ /dev/null @@ -1,8 +0,0 @@ -"""Version information.""" - -# License: BSD 3-Clause - -# The following line *must* be the last in the module, exactly as formatted: -from __future__ import annotations - -__version__ = "0.16.0" diff --git a/openml/setups/setup.py b/openml/setups/setup.py index 170838138..20923db16 100644 --- a/openml/setups/setup.py +++ b/openml/setups/setup.py @@ -1,74 +1,101 @@ # License: BSD 3-Clause from __future__ import annotations -from dataclasses import asdict, dataclass from typing import Any import openml.config import openml.flows +from openml.base import OpenMLBase -@dataclass -class OpenMLSetup: +class OpenMLSetup(OpenMLBase): """Setup object (a.k.a. Configuration). + An OpenML Setup corresponds to a flow with a specific parameter configuration. + It inherits :class:`~openml.base.OpenMLBase` to gain shared functionality + like tagging, URL access and ``__repr__``. + Parameters ---------- setup_id : int - The OpenML setup id + The OpenML setup id. flow_id : int - The flow that it is build upon - parameters : dict - The setting of the parameters + The flow that it is built upon. + parameters : dict or None + The setting of the parameters, mapping parameter id to + :class:`OpenMLParameter`. """ - setup_id: int - flow_id: int - parameters: dict[int, Any] | None - - def __post_init__(self) -> None: - if not isinstance(self.setup_id, int): + def __init__( + self, + setup_id: int, + flow_id: int, + parameters: dict[int, Any] | None, + ) -> None: + if not isinstance(setup_id, int): raise ValueError("setup id should be int") - - if not isinstance(self.flow_id, int): + if not isinstance(flow_id, int): raise ValueError("flow id should be int") - - if self.parameters is not None and not isinstance(self.parameters, dict): + if parameters is not None and not isinstance(parameters, dict): raise ValueError("parameters should be dict") + self.setup_id = setup_id + self.flow_id = flow_id + self.parameters = parameters + + @property + def id(self) -> int | None: + """The setup id, unique among OpenML setups.""" + return self.setup_id + + @classmethod + def _entity_letter(cls) -> str: + """Return the letter used in OpenML URLs for setups (``'s'``).""" + return "s" + + def _get_repr_body_fields( + self, + ) -> list[tuple[str, str | int | list[str] | None]]: + """Return fields shown in :meth:`__repr__`.""" + n_params: int | str = len(self.parameters) if self.parameters is not None else "nan" + return [ + ("Setup ID", self.setup_id), + ("Flow ID", self.flow_id), + ("Flow URL", openml.flows.OpenMLFlow.url_for_id(self.flow_id)), + ("# of Parameters", n_params), + ] + def _to_dict(self) -> dict[str, Any]: + """Return a dictionary representation of this setup.""" return { "setup_id": self.setup_id, "flow_id": self.flow_id, - "parameters": {p.id: p._to_dict() for p in self.parameters.values()} - if self.parameters is not None - else None, - } - - def __repr__(self) -> str: - header = "OpenML Setup" - header = f"{header}\n{'=' * len(header)}\n" - - fields = { - "Setup ID": self.setup_id, - "Flow ID": self.flow_id, - "Flow URL": openml.flows.OpenMLFlow.url_for_id(self.flow_id), - "# of Parameters": ( - len(self.parameters) if self.parameters is not None else float("nan") + "parameters": ( + {p.id: p._to_dict() for p in self.parameters.values()} + if self.parameters is not None + else None ), } - # determines the order in which the information will be printed - order = ["Setup ID", "Flow ID", "Flow URL", "# of Parameters"] - _fields = [(key, fields[key]) for key in order if key in fields] + def _parse_publish_response(self, xml_response: dict[str, str]) -> None: + """Setups cannot be published; raises :class:`NotImplementedError`.""" + raise NotImplementedError("OpenML Setups cannot be published.") - longest_field_name_length = max(len(name) for name, _ in _fields) - field_line_format = f"{{:.<{longest_field_name_length}}}: {{}}" - body = "\n".join(field_line_format.format(name, value) for name, value in _fields) - return header + body + def publish(self) -> OpenMLBase: + """Setups cannot be published to the server. + + Raises + ------ + TypeError + Always raised — OpenML Setups are created server-side and cannot be + uploaded by the client. + """ + raise TypeError( + "OpenML Setups are created automatically when a run is published " + "and cannot be uploaded directly." + ) -@dataclass class OpenMLParameter: """Parameter object (used in setup). @@ -76,9 +103,9 @@ class OpenMLParameter: ---------- input_id : int The input id from the openml database - flow id : int + flow_id : int The flow to which this parameter is associated - flow name : str + flow_name : str The name of the flow (no version number) to which this parameter is associated full_name : str @@ -94,33 +121,47 @@ class OpenMLParameter: If the parameter was set, the value that it was set to. """ - input_id: int - flow_id: int - flow_name: str - full_name: str - parameter_name: str - data_type: str - default_value: str - value: str - - def __post_init__(self) -> None: + def __init__( # noqa: PLR0913 + self, + input_id: int, + flow_id: int, + flow_name: str, + full_name: str, + parameter_name: str, + data_type: str, + default_value: str, + value: str, + ) -> None: + self.input_id = input_id + self.flow_id = flow_id + self.flow_name = flow_name + self.full_name = full_name + self.parameter_name = parameter_name + self.data_type = data_type + self.default_value = default_value + self.value = value # Map input_id to id for backward compatibility self.id = self.input_id def _to_dict(self) -> dict[str, Any]: - result = asdict(self) - # Replaces input_id with id for backward compatibility - result["id"] = result.pop("input_id") - return result + return { + "id": self.input_id, + "flow_id": self.flow_id, + "flow_name": self.flow_name, + "full_name": self.full_name, + "parameter_name": self.parameter_name, + "data_type": self.data_type, + "default_value": self.default_value, + "value": self.value, + } def __repr__(self) -> str: header = "OpenML Parameter" header = f"{header}\n{'=' * len(header)}\n" - fields = { + fields: dict[str, Any] = { "ID": self.id, "Flow ID": self.flow_id, - # "Flow Name": self.flow_name, "Flow Name": self.full_name, "Flow URL": openml.flows.OpenMLFlow.url_for_id(self.flow_id), "Parameter Name": self.parameter_name,