Skip to content

Commit 7efee3c

Browse files
authored
Merge branch 'main' into enhance-docstrings
2 parents 8740364 + aa04b30 commit 7efee3c

8 files changed

Lines changed: 280 additions & 51 deletions

File tree

.github/workflows/test.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ jobs:
106106

107107
- name: Run tests on Ubuntu Test
108108
if: matrix.os == 'ubuntu-latest'
109+
env:
110+
OPENML_TEST_SERVER_ADMIN_KEY: ${{ secrets.OPENML_TEST_SERVER_ADMIN_KEY }}
109111
run: |
110112
if [ "${{ matrix.code-cov }}" = "true" ]; then
111113
codecov="--cov=openml --long --cov-report=xml"
@@ -121,6 +123,8 @@ jobs:
121123
122124
- name: Run tests on Ubuntu Production
123125
if: matrix.os == 'ubuntu-latest'
126+
env:
127+
OPENML_TEST_SERVER_ADMIN_KEY: ${{ secrets.OPENML_TEST_SERVER_ADMIN_KEY }}
124128
run: |
125129
if [ "${{ matrix.code-cov }}" = "true" ]; then
126130
codecov="--cov=openml --long --cov-report=xml"
@@ -136,6 +140,8 @@ jobs:
136140
137141
- name: Run tests on Windows
138142
if: matrix.os == 'windows-latest'
143+
env:
144+
OPENML_TEST_SERVER_ADMIN_KEY: ${{ secrets.OPENML_TEST_SERVER_ADMIN_KEY }}
139145
run: | # we need a separate step because of the bash-specific if-statement in the previous one.
140146
pytest -n 4 --durations=20 --dist load -sv --reruns 5 --reruns-delay 1 -m "not uses_test_server"
141147

CONTRIBUTING.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,17 @@ To test your new contribution, add [unit tests](https://github.com/openml/openml
9696
* Please ensure that the example is run on the test server by beginning with the call to `openml.config.start_using_configuration_for_example()`, which is done by default for tests derived from `TestBase`.
9797
* Add the `@pytest.mark.sklearn` marker to your unit tests if they have a dependency on scikit-learn.
9898
99+
#### Running Tests That Require Admin Privileges
100+
101+
Some tests require admin privileges on the test server and will be automatically skipped unless you provide an admin API key. For regular contributors, the tests will skip gracefully. For core contributors who need to run these tests locally, you can set up the key by exporting the variable as below before running the tests:
102+
103+
```bash
104+
# For windows
105+
$env:OPENML_TEST_SERVER_ADMIN_KEY = "admin-key"
106+
# For linux/mac
107+
export OPENML_TEST_SERVER_ADMIN_KEY="admin-key"
108+
```
109+
99110
### Pull Request Checklist
100111
101112
You can go to the `openml-python` GitHub repository to create the pull request by [comparing the branch](https://github.com/openml/openml-python/compare) from your fork with the `develop` branch of the `openml-python` repository. When creating a pull request, make sure to follow the comments and structured provided by the template on GitHub.

openml/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
OPENML_CACHE_DIR_ENV_VAR = "OPENML_CACHE_DIR"
2727
OPENML_SKIP_PARQUET_ENV_VAR = "OPENML_SKIP_PARQUET"
28+
OPENML_TEST_SERVER_ADMIN_KEY_ENV_VAR = "OPENML_TEST_SERVER_ADMIN_KEY"
2829
_TEST_SERVER_NORMAL_USER_KEY = "normaluser"
2930

3031

openml/testing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class TestBase(unittest.TestCase):
4848
}
4949
flow_name_tracker: ClassVar[list[str]] = []
5050
test_server = "https://test.openml.org/api/v1/xml"
51-
admin_key = "abc"
51+
admin_key = os.environ.get(openml.config.OPENML_TEST_SERVER_ADMIN_KEY_ENV_VAR)
5252
user_key = openml.config._TEST_SERVER_NORMAL_USER_KEY
5353

5454
# creating logger for tracking files uploaded to test server

openml/utils.py

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@
22
from __future__ import annotations
33

44
import contextlib
5+
import re
56
import shutil
67
import warnings
7-
from collections.abc import Callable, Mapping, Sized
8+
from abc import ABC, abstractmethod
9+
from collections.abc import Callable, Iterable, Mapping, Sequence, Sized
810
from functools import wraps
911
from pathlib import Path
10-
from typing import TYPE_CHECKING, Any, Literal, TypeVar, overload
12+
from typing import (
13+
TYPE_CHECKING,
14+
Any,
15+
Literal,
16+
TypeVar,
17+
overload,
18+
)
1119
from typing_extensions import ParamSpec
1220

1321
import numpy as np
@@ -470,3 +478,57 @@ def update(self, length: int) -> None:
470478
self._progress_bar.update(length)
471479
if self._progress_bar.total <= self._progress_bar.n:
472480
self._progress_bar.close()
481+
482+
483+
class ReprMixin(ABC):
484+
"""A mixin class that provides a customizable string representation for OpenML objects.
485+
486+
This mixin standardizes the __repr__ output format across OpenML classes.
487+
Classes inheriting from this mixin should implement the
488+
_get_repr_body_fields method to specify which fields to display.
489+
"""
490+
491+
def __repr__(self) -> str:
492+
body_fields = self._get_repr_body_fields()
493+
return self._apply_repr_template(body_fields)
494+
495+
@abstractmethod
496+
def _get_repr_body_fields(self) -> Sequence[tuple[str, str | int | list[str] | None]]:
497+
"""Collect all information to display in the __repr__ body.
498+
499+
Returns
500+
-------
501+
body_fields : List[Tuple[str, Union[str, int, List[str]]]]
502+
A list of (name, value) pairs to display in the body of the __repr__.
503+
E.g.: [('metric', 'accuracy'), ('dataset', 'iris')]
504+
If value is a List of str, then each item of the list will appear in a separate row.
505+
"""
506+
# Should be implemented in the base class.
507+
508+
def _apply_repr_template(
509+
self,
510+
body_fields: Iterable[tuple[str, str | int | list[str] | None]],
511+
) -> str:
512+
"""Generates the header and formats the body for string representation of the object.
513+
514+
Parameters
515+
----------
516+
body_fields: List[Tuple[str, str]]
517+
A list of (name, value) pairs to display in the body of the __repr__.
518+
"""
519+
# We add spaces between capitals, e.g. ClassificationTask -> Classification Task
520+
name_with_spaces = re.sub(
521+
r"(\w)([A-Z])",
522+
r"\1 \2",
523+
self.__class__.__name__[len("OpenML") :],
524+
)
525+
header_text = f"OpenML {name_with_spaces}"
526+
header = f"{header_text}\n{'=' * len(header_text)}\n"
527+
528+
_body_fields: list[tuple[str, str | int | list[str]]] = [
529+
(k, "None" if v is None else v) for k, v in body_fields
530+
]
531+
longest_field_name_length = max(len(name) for name, _ in _body_fields)
532+
field_line_format = f"{{:.<{longest_field_name_length}}}: {{}}"
533+
body = "\n".join(field_line_format.format(name, value) for name, value in _body_fields)
534+
return header + body

tests/test_datasets/test_dataset_functions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,10 @@ def _assert_status_of_dataset(self, *, did: int, status: str):
599599
assert len(result) == 1
600600
assert result[did]["status"] == status
601601

602+
@pytest.mark.skipif(
603+
not os.environ.get(openml.config.OPENML_TEST_SERVER_ADMIN_KEY_ENV_VAR),
604+
reason="Test requires admin key. Set OPENML_TEST_SERVER_ADMIN_KEY environment variable.",
605+
)
602606
@pytest.mark.flaky()
603607
@pytest.mark.uses_test_server()
604608
def test_data_status(self):

0 commit comments

Comments
 (0)