Skip to content

Timeout parameters are accepted but not used #983

@idan3

Description

@idan3

Bug Report: Timeout parameters are accepted but not used

Summary

The RESTClient constructor accepts connect_timeout and read_timeout parameters, creates a urllib3.Timeout object, but never passes it to the PoolManager or uses it in HTTP requests. This means requests can hang indefinitely.

Environment

  • Package: massive==2.0.3
  • Python: 3.13
  • OS: macOS

Steps to Reproduce

from massive import RESTClient

# Create client with explicit timeouts
client = RESTClient(
    api_key="your_api_key",
    connect_timeout=5.0,
    read_timeout=5.0
)

# Check if timeout is actually used
print("Timeout object:", client.timeout)
print("PoolManager kwargs:", client.client.connection_pool_kw)

Expected Behavior

The timeout should be passed to either:

  1. urllib3.PoolManager(timeout=...) at construction, or
  2. self.client.request(..., timeout=self.timeout) on each request

Actual Behavior

Timeout object: Timeout(connect=5.0, read=5.0, total=None)
PoolManager kwargs: {'ca_certs': '...', 'cert_reqs': 'CERT_REQUIRED', 'retries': Retry(...)}

The timeout object is created but never used. The connection_pool_kw does not contain a timeout, and the _get method in base.py does not pass timeout to requests.

Root Cause

In massive/rest/base.py:

# Line 81: Timeout is created
self.timeout = urllib3.Timeout(connect=connect_timeout, read=read_timeout)

# Line 73-79: PoolManager is created WITHOUT timeout
self.client = urllib3.PoolManager(
    num_pools=num_pools,
    headers=self.headers,
    ca_certs=certifi.where(),
    cert_reqs="CERT_REQUIRED",
    retries=retry_strategy,
    # timeout=self.timeout  ← MISSING
)

# Line 119-124: Request is made WITHOUT timeout
resp = self.client.request(
    "GET",
    self.BASE + path,
    fields=params,
    headers=headers,
    # timeout=self.timeout  ← MISSING
)

Suggested Fix

Either pass timeout to PoolManager (applies to all requests):

self.client = urllib3.PoolManager(
    num_pools=num_pools,
    headers=self.headers,
    ca_certs=certifi.where(),
    cert_reqs="CERT_REQUIRED",
    retries=retry_strategy,
    timeout=self.timeout,  # Add this
)

Or pass timeout on each request (more flexible):

resp = self.client.request(
    "GET",
    self.BASE + path,
    fields=params,
    headers=headers,
    timeout=self.timeout,  # Add this
)

Impact

Without a working timeout, any network issue (slow server, DNS hang, etc.) can cause the SDK to hang indefinitely, blocking the calling thread.

Workaround

For async code, wrap SDK calls with asyncio.timeout():

import asyncio

async def get_data_with_timeout():
    async with asyncio.timeout(15):
        return await asyncio.to_thread(client.get_snapshot_ticker, ...)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions