Type(foo) errors out as NoneType, but it has content?

How is this possible? I have a variable that I can successfully print it’s values, class, and directory, yet asking what it’s type is errors out with None!

if dateCreated:
    # ...
    print(f"Patch_note: dateCreated: {dateCreated}")
    print(f"date created class:")
    print(dateCreated.__class__)
    print(f"date created dir:")
    print(dir(dateCreated))
    print(f"date created type:")
    print(type(dateCreated))

Console output:

Patch_note: dateCreated: 2013-03-07 00:00:00.073-0700
date created class:
<class 'str'>
date created dir:
['__add__', '__class__', ...snip....'title', 'translate', 'upper', 'zfill']
date created type:
Traceback (most recent call last):
...snip...
  File "A:\code\trilium-py\src\trilium_py\client.py", line 371, in patch_note
    print(type(dateCreated))
          ^^^^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not callable

Python 3.12.4 on Windows.
Full code: trilium-py/src/trilium_py/client.py at 196d3d9e6097a00e30227184f5b50e016485b84d · maphew/trilium-py · GitHub

You’re shadowing the builtin type with the argument type which defaults to None.

It is type the one that is None and type(dateCreated) is trying to call it.

ohhhhhhhh, damn. Thanks! I’ve been busting my head on this for an hour, and never thought to look at that.

For watchers, here is where the shadow was cast:

    def patch_note(
        self,
        noteId: str,
        title: Optional[str] = None,
        type: Optional[str] = None,   # <------ shadowing
        mime: Optional[str] = None,
        dateCreated: Optional[datetime] = None,

src: trilium-py/src/trilium_py/client.py at 196d3d9e6097a00e30227184f5b50e016485b84d · maphew/trilium-py · GitHub

It doesn’t “error out with None”. It errors out by telling you that 'NoneType' object is not callable.

Think about that carefully.

When you write type(dateCreated) (the underlined part), which thing is being “called” - dateCreated, or type?

The answer is, type is being called.

So, something else in your code has set type to None.

Please keep in mind that all the names of built-in types and functions are not reserved words. You won’t get a SyntaxError from type = None somewhere else, because as far as Python is concerned, type is just another name. (Long ago, True and False were also assignable names, rather than special literal syntax.)

In short, you have this problem of shadowing:

I guess others pointed this out to you already; but I wanted to highlight the fact that solving problems starts with reading the error message (not just immediately putting it in a mental box like “oh, it’s something to do with None”); and then the next step is to think about how the described problem relates to the highlighted code (where’s the call?).

3 Likes

Thank you for taking the time flesh out a teaching explanation. You can be assured I’ll remember it!

Is there an idiomatic alternative word to use for type-as-variable instead?

kind comes to mind for me personally but I doubt it’s good for general use.

The issue here isn’t about category theory (where “kind” would be a meta-type for types), so that isn’t applicable.

Generally we just say “the type of a value” (Python’s names don’t have types, but the values do), or “the name/variable type” (the formatting helps!) or “the type builtin/function” (meaning, the object that type names by default - it isn’t actually a function, but that gets tricky to explain in precise terms because it’s implemented within the interpreter rather than as Python code). It depends on what we actually mean :slight_smile: