Skip to content

Errors when testing pyDataverse 0.4.0.beta1 using pytest: HTTP 401 - Bad API key #246

Description

@liujinjie111

As discussed in 7/15 pyDataverse meeting, I got error when testing pyDataverse 0.4.0.beta1 using pytest.

I cloned https://github.com/gdcc/pyDataverse and checked out the specific commit 7f56e219f6158eafb61711c7419b1ee12d068993. I created a local virtual environment in which I installed dependencies. I expanded and modified tests/dataverse/test_dataverse.py to test dataverse.py, run tests in pytest framework. I found:
test_collections(), test_datasets(), test_search(), test_create_collection(), test_create_dataset(), test_fetch_dataset(), test_fetch_collection(), test_from_dataverse_dict(), test_getitem_collection(), test_getitem_dataset(), test_getitem_invalid_identifier(), test_view_getitem_collection(), test_view_getitem_dataset() failed the test. ERROR pyDataverse.api.api:logger.py:74 [red]✗[/red] ERROR: HTTP 401 - Bad API key.

Below is the traceback for test_collections(). Other functions have the similar/same tracebacks.

Running pytest with args: ['-p', 'vscode_pytest', '--rootdir=d:\\pyDataverse', 'd:\\pyDataverse\\tests\\dataverse\\test_dataverse.py::TestDataverse::test_collections']
============================= test session starts =============================
platform win32 -- Python 3.10.6, pytest-9.1.1, pluggy-1.6.0 -- d:\pyDataverse\venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: d:\pyDataverse
configfile: pyproject.toml
plugins: anyio-4.14.1, asyncio-1.3.0, cov-5.0.0, sugar-1.1.1
asyncio: mode=auto, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
collecting ... collected 1 item

tests/dataverse/test_dataverse.py::TestDataverse::test_collections FAILED [100%]

================================== FAILURES ===================================
_______________________ TestDataverse.test_collections ________________________

self = <tests.dataverse.test_dataverse.TestDataverse object at 0x000001F12338C4F0>
dataverse = Dataverse(base_url='https://demo.dataverse.org', with token)
collection = <function collection.<locals>.create_collection at 0x000001F1235AE0E0>

    def test_collections(
        self,
        dataverse: Dataverse,
        collection: CollectionFactory,
    ):
        """Test accessing collections from root.
    
        Verifies that collections can be accessed from the root Dataverse instance
        via the collections property.
        """
    
        collection_alias = str(uuid.uuid4())
        print(dataverse.api_token)
        print(dataverse.native_api.api_token)
        print(dataverse.native_api.model_dump())
        print(dataverse.native_api.auth)
        print(type(dataverse.native_api.auth))
    
>       test_collection = collection(collection_alias)

tests\dataverse\test_dataverse.py:159: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests\conftest.py:395: in create_collection
    collection = dataverse.create_collection(
pyDataverse\dataverse\dataverse.py:530: in create_collection
    response = self.native_api.create_collection(
pyDataverse\api\native.py:146: in create_collection
    response = self.post_request(
pyDataverse\api\api.py:718: in post_request
    return self._sync_request(
pyDataverse\api\api.py:1298: in _sync_request
    return self._handle_response(response, model, is_collection)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = NativeApi(logger=<pyDataverse.api.utilities.logger.ApiLogger object at 0x000001F12338EC50>, base_url='https://demo.dat...verse.org/api/', base_url_api_native='https://demo.dataverse.org/api/', api_base_url='https://demo.dataverse.org/api/')
resp = <Response [401 Unauthorized]>
model = <class 'pyDataverse.models.collection.create.CollectionCreateResponse'>
is_collection = False

    def _handle_response(
        self,
        resp: httpx.Response,
        model: Optional[Type[BaseModel] | Type[List[A]] | Type[bytes] | Type[str]],
        is_collection: bool,
    ):
        """
        Handles and processes HTTP response based on the provided model configuration.
    
        Args:
            resp: The HTTP response object to process.
            model: The Pydantic model class to use for validation and parsing.
                If None, returns the raw response.
            is_collection: Whether the response should be treated as a
                collection/list of model instances.
    
        Returns:
            If model is provided and is_collection is True, returns a list of
            validated model instances. If model is provided and is_collection
            is False, returns a single validated model instance. If model is
            None, returns the raw httpx.Response.
    
        Raises:
            HTTPStatusError: If the response indicates an error status.
        """
    
        try:
            body = resp.json()
            text = None
        except (json.JSONDecodeError, UnicodeDecodeError):
            body = {}
            if model is bytes:
                text = resp.content
            else:
                text = resp.text
    
        # Raise for status if not 200 and no error message present
        if resp.is_error and "message" not in body:
            self.logger.error(f"ERROR: HTTP {resp.status_code} - {resp.text}")
            resp.raise_for_status()
    
        if text is not None:
            return text
    
        # Parse response into APIResponse format
        try:
            content = APIResponse.model_validate(body)
        except ValidationError:
            content = APIResponse.from_out_of_format(
                data=body,
                status_code=resp.status_code,
            )
    
        # Check API-level status
        if content.status != Status.OK:
            self.logger.error(f"ERROR: HTTP {resp.status_code} - {content.message}")
>           raise HTTPStatusError(
                request=resp.request,
                response=resp,
                message=f"ERROR: HTTP {resp.status_code} - {content.message}",
            )
E           httpx.HTTPStatusError: ERROR: HTTP 401 - Bad API key

pyDataverse\api\api.py:1480: HTTPStatusError
---------------------------- Captured stdout setup ----------------------------
ℹ NativeApi: Connected to https://demo.dataverse.org
ℹ Fetching metadata blocks...
ℹ Dataset factory initialized
---------------------------- Captured stdout call -----------------------------
{'base_url': 'https://demo.dataverse.org', 'api_version': <APIVersion.LATEST: 'v1'>, 'auth': <pyDataverse.auth.ApiTokenAuth object at 0x000001F12338E8F0>, 'timeout': 500, 'max_connections': 10, 'max_keepalive_connections': 5, 'client': None, 'verbose': 1, 'base_url_api': 'https://demo.dataverse.org/api/', 'base_url_api_native': 'https://demo.dataverse.org/api/', 'api_base_url': 'https://demo.dataverse.org/api/'}
<pyDataverse.auth.ApiTokenAuth object at 0x000001F12338E8F0>
<class 'pyDataverse.auth.ApiTokenAuth'>
[07/16/26 18:48:04] ERROR    ✗ ERROR: HTTP 401 - Bad API key                   
------------------------------ Captured log call ------------------------------
ERROR    pyDataverse.api.api:logger.py:74 [red]✗[/red] ERROR: HTTP 401 - Bad API key
=========================== short test summary info ===========================
FAILED tests/dataverse/test_dataverse.py::TestDataverse::test_collections - h...
============================== 1 failed in 1.84s ==============================
Finished running tests!

Below are the dependencies:

(venv) (base) PS D:\pyDataverse> pip list                                                             
Package                  Version     Editable project location
------------------------ ----------- -------------------------
annotated-types          0.7.0
anyio                    4.14.1
asyncer                  0.0.10
backports.asyncio.runner 1.2.0
bigtree                  1.5.1
cachetools               6.2.6
certifi                  2026.6.17
cfgv                     3.5.0
colorama                 0.4.6
coverage                 7.15.0
deprecation              2.1.0
distlib                  0.4.3
dnspython                2.8.0
email-validator          2.3.0
exceptiongroup           1.3.1
filelock                 3.29.7
fsspec                   2026.6.0
h11                      0.16.0
httpcore                 1.0.9
httpx                    0.28.1
identify                 2.6.19
idna                     3.18
iniconfig                2.3.0
isodate                  0.7.2
markdown-it-py           4.2.0
mdurl                    0.1.2
nest-asyncio             1.6.0
nodeenv                  1.10.0
numpy                    2.2.6
packaging                26.2
pandas                   2.3.3
pip                      26.1.2
platformdirs             4.10.0
pluggy                   1.6.0
pre-commit               3.5.0
pydantic                 2.13.4
pydantic_core            2.46.4
pyDataverse              0.4.0b1     D:\pyDataverse
Pygments                 2.20.0
pyparsing                3.3.2
pytest                   9.1.1
pytest-asyncio           1.3.0
pytest-cov               5.0.0
pytest-sugar             1.1.1
python-dateutil          2.9.0.post0
python-discovery         1.4.4
pytz                     2026.2
PyYAML                   6.0.3
rdflib                   7.6.0
rich                     14.3.4
ruff                     0.4.4
setuptools               63.2.0
six                      1.17.0
sniffio                  1.3.1
termcolor                3.3.0
tomli                    2.4.1
typing_extensions        4.16.0
typing-inspection        0.4.2
tzdata                   2026.2
virtualenv               21.6.0
wheel                    0.43.0
(venv) (base) PS D:\pyDataverse> 

Metadata

Metadata

Assignees

No one assigned

    Labels

    status:incomingNewly created issue to be forwardedtype:bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions