Skip to content

Public string arguments refuse str subclasses (numpy.str_, str Enums) #154

Description

@mtopolnik

Summary

Every public string argument refuses a str subclass such as numpy.str_, a (str, Enum) member or a StrEnum member. Two different errors come out, depending on where the value is checked:

  1. Parameters typed str in Cython refuse at the call, with a clear message:

    TypeError: Argument 'table_name' has incorrect type (expected str, got numpy.str_)
    

    This applies to table_name (Sender.row, Sender.dataframe, QuestDB.dataframe, Sender.transaction, SenderTransaction), conf_str (Sender.from_conf, QuestDB.from_conf), host (Sender(...)), and sql (QuestDB.query, QuestDB.execute).

  2. Values checked with isinstance(x, str) and then handed to a str-typed variable pass the check and fail on the next line. The error doesn't name the argument:

    TypeError: Expected str, got numpy.str_
    

    Over ILP it's wrapped as QuestDBError: Expected str, got numpy.str_. This applies to dataframe(at=...) and dataframe(symbols=[...]) (through _dataframe_get_loc(df, str col_name, ...)), and to the symbol and column names and values in Sender.row.

Both behaviors date from before 5.0. They aren't regressions.

Why it matters

numpy.str_ turns up whenever a name comes out of an array, e.g. at=np.array(['ts'])[0] or a table name taken from np.unique(df['tbl']). (str, Enum) is a common way to keep table and column names in one place. Both are str by every isinstance test, so the refusal is surprising. In case 2 the message doesn't even say which argument is at fault.

Reproduction

import numpy as np, pandas as pd, questdb

df = pd.DataFrame({'v': [1], 'ts': pd.to_datetime(['2025-01-01'])})
with questdb.connect('ws::addr=localhost:9000;') as db:
    db.dataframe(df, table_name='t', at=np.str_('ts'))
    # TypeError: Expected str, got numpy.str_
    db.dataframe(df, table_name=np.str_('t'), at='ts')
    # TypeError: Argument 'table_name' has incorrect type (expected str, got numpy.str_)

Suggested direction

Normalize every public string argument once, at the API boundary, then keep the internal str typing as it is:

  • convert with x if type(x) is str else str.__str__(x), not with str(x);
  • str() calls the subclass's own __str__, and for a (str, Enum) member that returns 'Kind.NAME' rather than the value.

Converting at the boundary also removes the inconsistency in case 2. Within one dataframe() call, at and table_name would then follow the same rule.

The df.attrs['questdb'] claim kind had the same problem. It was fixed in #140 (Moderate 5 of the 2026-09-30 review) by normalizing in _roundtrip_kind. That one couldn't wait, because a claim travels with the data and must never fail a write.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions