pymc-devs / pymc-devs/pytensor

pt.tensor("x") without shape= gives misleading error — default implies optional when it isn't

Open Beginner friendly
#2,338 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
644
Forks
208
Avg merge
2d 14h
Merged PRs (30d)
16

Description

The problem

A user who calls `pt.tensor("x")` without a `shape` argument gets:

TypeError: 'NoneType' object is not iterable

This is opaque. The user hasn't passed `None`, they've omitted an argument that appears optional. The function signature says `shape: ... | None = None`, which tells them "this is optional." It isn't — `shape` is effectively required, because `TensorType` has no way to infer dimensionality without it.

The same applies directly:

>>> TensorType("float64")
TypeError: 'NoneType' object is not iterable
Where

`TensorType.init` iterates over `shape` unconditionally:

`type.py:121-123`

if isinstance(shape, int):
    shape = (shape,)
self.shape = tuple(parse_bcast_and_shape(s) for s in shape)   # shape=None → crash

There's a guard for `int`, but no guard for `None`. The default flows straight into the loop.

Contrast

`pt.xtensor("x", dims=("a",))` works without `shape` because `XTensorType` knows `ndim` from `dims` and fills a default. `TensorType` has no equivalent — which is fine — but the error should reflect that.

What's confusing
  1. The signature is misleading: `shape: ... | None = None` declares `None` as the default and a valid type. It's neither.
  2. The error says nothing useful: "'NoneType' object is not iterable" tells users nothing about what they did wrong or how to fix it.
  3. No pointer to helpers: the error doesn't mention `shape=` or the shorthand constructors.
Proposed fix

Remove the default from `shape` in both `tensor()` and `TensorType.init`, and drop `| None` from the type hints. Python then gives its native message:

# tensor()
def tensor(name=None, *, dtype=None, shape, **kwargs):
    ...

# TensorType.__init__
def __init__(self, dtype, shape, name=None, broadcastable=None):
    ...

Result:

>>> pt.tensor("x")
TypeError: tensor() missing 1 required keyword-only argument: 'shape'

>>> TensorType("float64")
TypeError: TensorType.__init__() missing 1 required positional argument: 'shape'

Zero new code, zero breakage (every call site in the repo already passes `shape=`), and the deprecated `broadcastable` pathway still works since `broadcastable` has its own default and gets assigned to `shape` before use.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with TensorType.init at pytensor/tensor/type.py:121-123, then locate the tensor() entry point and inspect how both signatures handle shape. Verify the existing call sites described in the issue and confirm that omitted shape produces Python's missing-argument errors while the deprecated broadcastable pathway remains supported.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
machine-learning
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.