Skip to content

Commit 8de78af

Browse files
committed
merge main
2 parents 015cca3 + 7feb2a3 commit 8de78af

29 files changed

Lines changed: 322 additions & 361 deletions

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ jobs:
170170
env:
171171
OPENML_TEST_SERVER_ADMIN_KEY: ${{ secrets.OPENML_TEST_SERVER_ADMIN_KEY }}
172172
run: | # we need a separate step because of the bash-specific if-statement in the previous one.
173-
pytest -n 4 --durations=20 --dist load -sv --reruns 5 --reruns-delay 1 -m "not uses_test_server"
173+
pytest -n 4 --durations=20 --dist load -sv --reruns 5 --reruns-delay 1 -m "not test_server"
174174
175175
- name: Upload coverage
176176
if: matrix.code-cov && always()

docs/developer_setup.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@ The OpenML Python SDK utilizes `pytest` markers to categorize tests based on dep
170170
| Marker | Description |
171171
|-------------------|-----------------------------------------------------------------------------|
172172
| `sklearn` | Tests requiring `scikit-learn`. Skipped if the library is missing. |
173-
| `production` | Tests that interact with the live OpenML server (real API calls). |
174-
| `uses_test_server` | Tests requiring the OpenML test server environment. |
173+
| `production_server`| Tests that interact with the live OpenML server (real API calls). |
174+
| `test_server` | Tests requiring the OpenML test server environment. |
175175

176176
### Execution Examples
177177

@@ -190,7 +190,7 @@ pytest -m sklearn
190190
Exclude production tests (local only):
191191

192192
```bash
193-
pytest -m "not production"
193+
pytest -m "not production_server"
194194
```
195195

196196
### Admin Privilege Tests

openml/_api_calls.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
from . import config
2323
from .__version__ import __version__
2424
from .exceptions import (
25+
OpenMLAuthenticationError,
2526
OpenMLHashException,
26-
OpenMLNotAuthorizedError,
2727
OpenMLServerError,
2828
OpenMLServerException,
2929
OpenMLServerNoResult,
@@ -515,11 +515,7 @@ def __parse_server_exception(
515515
400, # run/42 delete
516516
460, # task/42 delete
517517
]:
518-
msg = (
519-
f"The API call {url} requires authentication via an API key.\nPlease configure "
520-
"OpenML-Python to use your API as described in this example:"
521-
"\nhttps://openml.github.io/openml-python/latest/examples/Basics/introduction_tutorial/#authentication"
522-
)
523-
return OpenMLNotAuthorizedError(message=msg)
518+
msg = f"The API call {url} requires authentication via an API key."
519+
return OpenMLAuthenticationError(message=msg)
524520

525521
return OpenMLServerException(code=code, message=full_message, url=url)

openml/cli.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,15 @@ def check_apikey(apikey: str) -> str:
102102

103103
def configure_server(value: str) -> None:
104104
def check_server(server: str) -> str:
105-
is_shorthand = server in ["test", "production"]
105+
is_shorthand = server in ["test", "production_server"]
106106
if is_shorthand or looks_like_url(server):
107107
return ""
108-
return "Must be 'test', 'production' or a url."
108+
return "Must be 'test', 'production_server' or a url."
109109

110110
def replace_shorthand(server: str) -> str:
111111
if server == "test":
112112
return f"{config.TEST_SERVER_URL}/api/v1/xml"
113-
if server == "production":
113+
if server == "production_server":
114114
return "https://www.openml.org/api/v1/xml"
115115
return server
116116

@@ -119,7 +119,7 @@ def replace_shorthand(server: str) -> str:
119119
value=value,
120120
check_with_message=check_server,
121121
intro_message="Specify which server you wish to connect to.",
122-
input_message="Specify a url or use 'test' or 'production' as a shorthand: ",
122+
input_message="Specify a url or use 'test' or 'production_server' as a shorthand: ",
123123
sanitize=replace_shorthand,
124124
)
125125

openml/exceptions.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,28 @@ class OpenMLNotAuthorizedError(OpenMLServerError):
6363
"""Indicates an authenticated user is not authorized to execute the requested action."""
6464

6565

66+
class OpenMLAuthenticationError(OpenMLServerError):
67+
"""Exception raised when API authentication fails.
68+
69+
This typically occurs when:
70+
- No API key is configured
71+
- The API key is invalid or expired
72+
- The API key format is incorrect
73+
74+
This is different from authorization (OpenMLNotAuthorizedError), which occurs
75+
when a valid API key lacks permissions for the requested operation.
76+
"""
77+
78+
def __init__(self, message: str):
79+
help_text = (
80+
"\n\nTo fix this:\n"
81+
"1. Get your API key from https://www.openml.org/\n"
82+
" (you'll need to register for a free account if you don't have one)\n"
83+
"2. Configure your API key by following the authentication guide:\n"
84+
" https://openml.github.io/openml-python/latest/examples/Basics/introduction_tutorial/#authentication"
85+
)
86+
super().__init__(message + help_text)
87+
88+
6689
class ObjectNotPublishedError(PyOpenMLError):
6790
"""Indicates an object has not been published yet."""

openml/tasks/functions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,9 @@ def get_task(
426426
# Including class labels as part of task meta data handles
427427
# the case where data download was initially disabled
428428
if isinstance(task, (OpenMLClassificationTask, OpenMLLearningCurveTask)):
429+
assert task.target_name is not None, (
430+
"Supervised tasks must define a target feature before retrieving class labels."
431+
)
429432
task.class_labels = dataset.retrieve_class_labels(task.target_name)
430433
# Clustering tasks do not have class labels
431434
# and do not offer download_split
@@ -599,6 +602,7 @@ def create_task(
599602
)
600603

601604
return task_cls(
605+
task_id=None,
602606
task_type_id=task_type,
603607
task_type="None", # TODO: refactor to get task type string from ID.
604608
data_set_id=dataset_id,

0 commit comments

Comments
 (0)