# How should we mark up multiple types in a type field?

**URL:** <https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196>\
**Category:** Documentation\
**Tags:** sphinx\
**Created:** [March 11, 2024, 1:36pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196 "2024-03-11T13:36:26Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![erlendaasland](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/erlendaasland/32/4378_2.png) [@erlendaasland](https://discuss.python.org/u/erlendaasland)\
**Post date:** [March 11, 2024, 1:36pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/1 "2024-03-11T13:36:26Z")

</div>

When using the `:type:` and `:rtype:` directives, how do we want to mark up multiple types?

Currently, we’re using a bar (`|`), similar to how you’d annotate a union of types:

```python
:param p:
   A parameter that takes an int or a float argument.
:type p: int | float

:param other:
   Possibly a path.
:type other: path-like object | None

```

However, [the Sphinx docs](https://www.sphinx-doc.org/en/master/usage/domains/python.html#send_message) says to use the word `or`, like this:

```python
:param p:
   A parameter that takes an int or a float argument.
:type p: int or float

:param other:
   Possibly a path.
:type other: path-like object or None

```

The rendered docs look the same\[1\], so it is a matter of style. In [PR #116389](https://github.com/python/cpython/pull/116389), @alexwaygood pointed out that using `or` could be more readable for the casual docs reader.

It would be nice to settle on a single style and document it in the devguide.

_Poll ([view on site](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/1))_

* * *

1. other than the `|` or `or` separator

---

<div class="post-metadata">

**Author:** ![steve.dower](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steve.dower/32/56_2.png) [@steve.dower](https://discuss.python.org/u/steve.dower)\
**Post date:** [March 11, 2024, 5:03pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/2 "2024-03-11T17:03:32Z")

</div>

> [@erlendaasland](#):
>
> pointed out that using `or` could be more readable for the casual docs reader.

Which “casual docs reader” is reading the raw ReST and not the rendered docs?

---

<div class="post-metadata">

**Author:** ![AlexWaygood](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/alexwaygood/32/4722_2.png) [@AlexWaygood](https://discuss.python.org/u/AlexWaygood)\
**Post date:** [March 11, 2024, 5:06pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/3 "2024-03-11T17:06:12Z")

</div>

Oh, the rendered docs look the same? Are they rendered “typing-style” or “Sphinx-style”? Can you edit a screenshot into your original post?

---

<div class="post-metadata">

**Author:** ![steve.dower](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steve.dower/32/56_2.png) [@steve.dower](https://discuss.python.org/u/steve.dower)\
**Post date:** [March 11, 2024, 5:13pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/4 "2024-03-11T17:13:25Z")

</div>

Yeah, FWIW, I’d prefer they be _rendered_ Sphinx style, but am quite happy to type them in using vertical bars.

---

<div class="post-metadata">

**Author:** ![hugovk](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/hugovk/32/14505_2.png) [@hugovk](https://discuss.python.org/u/hugovk)\
**Post date:** [March 11, 2024, 7:50pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/5 "2024-03-11T19:50:36Z")

</div>

The rendered docs look like the source.

This:

```rst
   :type timeout: float or None

```

renders like this:

 ![image](https://us1.discourse-cdn.com/flex002/uploads/python1/original/3X/7/c/7c1560ba2857f57083bc1ce64c5729f93cef24d8.png)

And this:

```rst
   :type timeout: float | None

```

renders like this:

 ![image](https://us1.discourse-cdn.com/flex002/uploads/python1/original/3X/5/0/502559918c902babed51168ec381cefa942858d1.png)

---

<div class="post-metadata">

**Author:** ![CAM-Gerlach](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cam-gerlach/32/3688_2.png) [@CAM-Gerlach](https://discuss.python.org/u/CAM-Gerlach)\
**Post date:** [March 11, 2024, 7:54pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/6 "2024-03-11T19:54:54Z")

</div>

If that were the case I’d agree, but I believe what @erlendaasland was referring to was everything rendering the same, _other than the “or”/"| character_—I see how that could be unclear, as I had to go double-check myself.

With that being the case, I’d have to go with “or” as being clearer for readers, despite having used `|` before myself in the same context, as the meaning may not be obvious to newer users who may not be familiar with the syntax (particularly since the bar is currently italicized, which looks rather strange to my eye).

---

<div class="post-metadata">

**Author:** ![steve.dower](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steve.dower/32/56_2.png) [@steve.dower](https://discuss.python.org/u/steve.dower)\
**Post date:** [March 11, 2024, 8:05pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/7 "2024-03-11T20:05:47Z")

</div>

> [@CAM-Gerlach](#):
>
> If that were the case I’d agree, but I believe what @erlendaasland was referring to was everything rendering the same, _other than the “or”/"| character_—I see how that could be unclear, as I had to go double-check myself.

Ah, I see. Consider my vote changed, then

---

<div class="post-metadata">

**Author:** ![nedbat](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nedbat/32/8744_2.png) [@nedbat](https://discuss.python.org/u/nedbat)\
**Post date:** [March 11, 2024, 9:34pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/8 "2024-03-11T21:34:45Z")

</div>

`float | None` is what Python needs in the .py file, and `float or None` is a valid sequence of Python tokens that produce a SyntaxError. `float or None` could lead people to an [misleading readability](https://nedbatchelder.com/blog/201801/pythons_misleading_readability.html) trap.

So, “or” is more readable in English, but these are short annotation, and I think it would be better to parallel the actual syntax.

---

<div class="post-metadata">

**Author:** ![MegaIng](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/megaing/32/16162_2.png) [@MegaIng](https://discuss.python.org/u/MegaIng)\
**Post date:** [March 11, 2024, 9:45pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/9 "2024-03-11T21:45:34Z")

</div>

> [@nedbat](#):
>
> `float | None` is what Python needs in the .py file, and `float or None` is a valid sequence of Python tokens that produce a SyntaxError

No, it evaluates to `float`.

* * *

Also, as a comment on that blog post points out, the trap isn’t readability, it’s writability. I am honestly not too concerned with beginners trying to copy over `float or None` into their projects. If they try to use it as an annotation, it’s not going to do anything, unless they start using a type checker, in which case they have to learn a lot of stuff anyway (including the difference between `|` and `or`). The docs should be concerned with readability of the docs only, which is potentially relevant for people who haven’t learned anything about the actual typing parts of python ecosystem, or even the bitwise operator that is being “misused”.

---

<div class="post-metadata">

**Author:** ![CAM-Gerlach](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cam-gerlach/32/3688_2.png) [@CAM-Gerlach](https://discuss.python.org/u/CAM-Gerlach)\
**Post date:** [March 11, 2024, 9:50pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/10 "2024-03-11T21:50:45Z")

</div>

This is also why, I feel, displaying the type names as literals (monospace) would help add clarity, as it would make clear that `float` and `None` are Python literals, while “or” is just a normal prose word.

---

<div class="post-metadata">

**Author:** ![nedbat](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nedbat/32/8744_2.png) [@nedbat](https://discuss.python.org/u/nedbat)\
**Post date:** [March 11, 2024, 10:18pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/11 "2024-03-11T22:18:10Z")

</div>

> [@MegaIng](#):
>
> No, it evaluates to `float`.

Sorry, you are right, it evaluates to `float` but it is an error for a type checker.

> [@CAM-Gerlach](#):
>
> displaying the type names as literals (monospace) would help add clarity, as it would make clear that `float` and `None` are Python literals, while “or” is just a normal prose word.

I don’t think monospace/proportional/monospace is a clear enough indicator (especially to beginners) that this isn’t valid Python code. The more the docs mirror Python syntax, the better.

---

<div class="post-metadata">

**Author:** ![steve.dower](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/steve.dower/32/56_2.png) [@steve.dower](https://discuss.python.org/u/steve.dower)\
**Post date:** [March 11, 2024, 11:13pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/12 "2024-03-11T23:13:17Z")

</div>

> [@nedbat](#):
>
> I don’t think monospace/proportional/monospace is a clear enough indicator (especially to beginners) that this isn’t valid Python code. The more the docs mirror Python syntax, the better.

I agree with the first part, but disagree with the second 🙂

The less it looks like something unique to Python, the less likely it’ll be copied over or imitated. Using the vertical bar seems like something special, rather than prose, and if copied it’ll be very non-obvious that it isn’t doing anything (or is doing something unexpected).

Would be great if we can find some formatting that makes the “or” look like prose, though. Maybe an Oxford comma?

> timeout (float, or None) …

---

<div class="post-metadata">

**Author:** ![CAM-Gerlach](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cam-gerlach/32/3688_2.png) [@CAM-Gerlach](https://discuss.python.org/u/CAM-Gerlach)\
**Post date:** [March 11, 2024, 11:59pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/13 "2024-03-11T23:59:52Z")

</div>

Sorry, I’m confused. You say that using monospace for code literals and plain/italic for prose won’t be clear enough that the result isn’t literal Python code, particularly for beginners, but then go on the state “the more the docs mirror Python syntax, the better”, when that is the very concern motivating the former.

_Neither_ form is intended to be literal Python code nor be used as such, but rather to make clear _to readers_ what types functions accept and return. The types shown are presented for maximum clarity to readers, and may not necessary match the programmatic typeshed type annotations used under the hood for various reasons (the more complex of which can be challenging to comprehend for a non-expert, much less a beginner who’s never heard of type annotations), leading to subtle bugs if a users were ever to try to use them in code. Others use glossary terms or protocol descriptions (file-like object) to accurately describe the accepted types, such as e.g. code object in the linked PR. Using syntax and formatting that is _more_ clearly _not_ incorrectly confused with literal code makes that, as well as the actual meaning, more clear.

I can certainly I sympathize with the desire to mirror actual type annotation syntax, and as mentioned previously did so in the number of callables I documented types for in this way, for that reason. However, after considering the points raised by Alex and others, the increased clarity and avoidance of confusion for readers, particularly newer ones (which is the overriding consideration for reference documentation) with “or” seems to be a decisive advantage over mirroring the type annotation syntax, particularly for something that’s _not_ actually intended to be a literal programmatic type hint.

---

<div class="post-metadata">

**Author:** ![erlendaasland](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/erlendaasland/32/4378_2.png) [@erlendaasland](https://discuss.python.org/u/erlendaasland)\
**Post date:** [March 12, 2024, 12:46pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/14 "2024-03-12T12:46:13Z")

</div>

I see now that I failed the 101 on how to present a poll: I did not state how I intended to use the poll results. To be frank, I’m not sure how I intend to use the poll result 🙂 I propose we discuss this at the next docs meeting.

---

<div class="post-metadata">

**Author:** ![toonarmycaptain](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/toonarmycaptain/32/183_2.png) [@toonarmycaptain](https://discuss.python.org/u/toonarmycaptain)\
**Post date:** [March 12, 2024, 1:11pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/15 "2024-03-12T13:11:19Z")

</div>

Maybe there can be hidden text that’s apparent when copied, that has the explanation that the `or` isn’t python and this text shouldn’t be copied for use in code?

timeout (float, or None) `timeout (float, <or> None) - NB this or is prosaic only; this text shouldn't be copied as correct python syntax`

---

<div class="post-metadata">

**Author:** ![erlendaasland](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/erlendaasland/32/4378_2.png) [@erlendaasland](https://discuss.python.org/u/erlendaasland)\
**Post date:** [March 12, 2024, 1:20pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/16 "2024-03-12T13:20:23Z")

</div>

It looks to me that the sub-discussion about C&P from `:type:` fields is based on the premise that all `:type:` fields are marked up with typing compatible names\[1\]. However, this is not always the case. _A lot_ of functions and methods take path-like and file-like objects\[2\]. In order to be more fair towards these, I expanded the example in the OP with this case\[3\]:

```python
:param other:
   Possibly a path.
:type other: path-like object | None

```

vs.

```python
:param other:
   Possibly a path.
:type other: path-like object or None

```

Take this into account when discussing the pros and cons of each style.

* * *

1. `int`, `None`, etc. 

2. other examples would be callables, and other non-basic types, often explicitly marked up using `:term:` or `:ref:` 

3. note that path-like and file-like refs would normally be marked up using `:term:`, but I omitted this, since IMO it is not relevant for the discussion

---

<div class="post-metadata">

**Author:** ![nedbat](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nedbat/32/8744_2.png) [@nedbat](https://discuss.python.org/u/nedbat)\
**Post date:** [March 12, 2024, 8:37pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/17 "2024-03-12T20:37:01Z")

</div>

I see the point about some types not being actual Python types that can be used. Perhaps an approach would be: either the types should be actual Python types with pipes for alternation, so the entire annotation is Python code in monospace; or the types should be presented prosaically in proportional type. It’s the authors’ choice which to use.

Switching typefaces word by word is not a strong enough signal to be clear.

---

<div class="post-metadata">

**Author:** ![pitrou](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/pitrou/32/28_2.png) [@pitrou](https://discuss.python.org/u/pitrou)\
**Post date:** [March 12, 2024, 11:26pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/18 "2024-03-12T23:26:26Z")

</div>

My experience on a third-party project matches that of @erlendaasland above: type descriptions in docstrings are very likely not valid type annotations, as they use informal prose such as “bytes-like” or “buffer-like”.

We should probably not conflate documentation, meant for human readers and tailored for maximal comprehension by them, and type annotations, meant for machine checkers and optimized for correctness and accuracy (selectivity + specificity). They do not need to share the same conventions.

---

<div class="post-metadata">

**Author:** ![CAM-Gerlach](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/cam-gerlach/32/3688_2.png) [@CAM-Gerlach](https://discuss.python.org/u/CAM-Gerlach)\
**Post date:** [March 13, 2024, 9:35am UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/19 "2024-03-13T09:35:28Z")

</div>

Or, rather, with an appropriate glossary term cross-reference to clarify what this actually means, as such prose terms should generally be (of some form):

```rst
:param other:
   Possibly a path.
:type other: :term:`path-like object` | None

```

vs.

```rst
:param other:
   Possibly a path.
:type other: :term:`path-like object` or None

```

> [@nedbat](#):
>
> I see the point about some types not being actual Python types that can be used. Perhaps an approach would be: either the types should be actual Python types with pipes for alternation, so the entire annotation is Python code in monospace; or the types should be presented prosaically in proportional type.

Right, but as described above even if the types presented _are_ literal Python types, they quite often will not literally correspond to the function’s actual programmatic annotations in typeshed, which this would imply, and users should not be induced to treat them as such (rather than what they truly are, as many here have emphasized: a clear, concise _human-readable_ reference of the accepted/returned types).

Furthermore, this leads to inconsistencies in how types are presented between and possibly even within functions, leading to even more potential for reader confusion and being harder for other writers to emulate (a point which you aptly made in support of other efforts to make similar such things consistent).

> [@pitrou](#):
>
> type descriptions in docstrings are very likely not valid type annotations, as they use informal prose such as “bytes-like” or “buffer-like”.

Strongly agreed with everything there, though I do note it is important to define and cross-reference those terms and use them consistently (as the Python docs generally do), so that readers are not left guessing or assuming the author’s intent. While it needn’t be as rigorously precise as a programmatic type specification, as reference documentation it should still clearly and unambiguously communicate to readers what types are accepted/returned, if not directly then by reference.

---

<div class="post-metadata">

**Author:** ![erlendaasland](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/erlendaasland/32/4378_2.png) [@erlendaasland](https://discuss.python.org/u/erlendaasland)\
**Post date:** [March 13, 2024, 9:35pm UTC](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/20 "2024-03-13T21:35:19Z")

</div>

> [@CAM-Gerlach](#):
>
> Or, rather, with an appropriate glossary term cross-reference to clarify what this actually means, as such prose terms should generally be (of some form)

Sure, hence the last footnote in [my post](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196/16).

[Next page](https://discuss.python.org/t/how-should-we-mark-up-multiple-types-in-a-type-field/48196.md?page=2)
