Skip to content

Support PEP 695 generics #348

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/guide/users/checking.md
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ print(module.special_thing)

> Public object points to a different kind of object

Changing the kind (attribute, function, class, module) of a public object can *silently* break your users' code.
Changing the kind (type alias, attribute, function, class, module) of a public object can *silently* break your users' code.

```python title="before"
# your code
Expand Down
26 changes: 16 additions & 10 deletions docs/guide/users/extending.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ If the source code is not available (the modules are built-in or compiled), Grif

Griffe then follows the [Visitor pattern](https://www.wikiwand.com/en/Visitor_pattern) to walk the tree and extract information. For ASTs, Griffe uses its [Visitor agent][griffe.Visitor] and for object trees, it uses its [Inspector agent][griffe.Inspector].

Sometimes during the walk through the tree (depth-first order), both the visitor and inspector agents will trigger events. These events can be hooked on by extensions to alter or enhance Griffe's behavior. Some hooks will be passed just the current node being visited, others will be passed both the node and an instance of an [Object][griffe.Object] subclass, such as a [Module][griffe.Module], a [Class][griffe.Class], a [Function][griffe.Function], or an [Attribute][griffe.Attribute]. Extensions will therefore be able to modify these instances.
Sometimes during the walk through the tree (depth-first order), both the visitor and inspector agents will trigger events. These events can be hooked on by extensions to alter or enhance Griffe's behavior. Some hooks will be passed just the current node being visited, others will be passed both the node and an instance of an [Object][griffe.Object] subclass, such as a [Module][griffe.Module], a [Class][griffe.Class], a [Function][griffe.Function], an [Attribute][griffe.Attribute], or a [Type Alias][griffe.TypeAlias]. Extensions will therefore be able to modify these instances.

The following flow chart shows an example of an AST visit. The tree is simplified: actual trees have a lot more nodes like `if/elif/else` nodes, `try/except/else/finally` nodes, [and many more][ast.AST].

Expand Down Expand Up @@ -200,8 +200,8 @@ There are two **load events**:
There are 3 generic **analysis events**:

- [`on_node`][griffe.Extension.on_node]: The "on node" events are triggered when the agent (visitor or inspector) starts handling a node in the tree (AST or object tree).
- [`on_instance`][griffe.Extension.on_instance]: The "on instance" events are triggered when the agent just created an instance of [Module][griffe.Module], [Class][griffe.Class], [Function][griffe.Function], or [Attribute][griffe.Attribute], and added it as a member of its parent. The "on instance" event is **not** triggered when an [Alias][griffe.Alias] is created.
- [`on_members`][griffe.Extension.on_members]: The "on members" events are triggered when the agent just finished handling all the members of an object. Functions and attributes do not have members, so there are no "on members" events for these two kinds.
- [`on_instance`][griffe.Extension.on_instance]: The "on instance" events are triggered when the agent just created an instance of [Module][griffe.Module], [Class][griffe.Class], [Function][griffe.Function], [Attribute][griffe.Attribute], or [Type Alias][griffe.TypeAlias], and added it as a member of its parent. The "on instance" event is **not** triggered when an [Alias][griffe.Alias] is created.
- [`on_members`][griffe.Extension.on_members]: The "on members" events are triggered when the agent just finished handling all the members of an object. Functions, attributes and type aliases do not have members, so there are no "on members" events for these kinds.

There are also specific **analysis events** for each object kind:

Expand All @@ -215,6 +215,8 @@ There are also specific **analysis events** for each object kind:
- [`on_function_instance`][griffe.Extension.on_function_instance]
- [`on_attribute_node`][griffe.Extension.on_attribute_node]
- [`on_attribute_instance`][griffe.Extension.on_attribute_instance]
- [`on_type_alias_node`][griffe.Extension.on_type_alias_node]
- [`on_type_alias_instance`][griffe.Extension.on_type_alias_instance]

And a special event for aliases:

Expand Down Expand Up @@ -315,7 +317,7 @@ class MyExtension(Extension):

The preferred method is to check the type of the received node rather than the agent.

Since hooks also receive instantiated modules, classes, functions and attributes, most of the time you will not need to use the `node` argument other than for checking its type and deciding what to do based on the result. And since we always add `**kwargs` to the hooks' signatures, you can drop any parameter you don't use from the signature:
Since hooks also receive instantiated modules, classes, functions, attributes and type aliases, most of the time you will not need to use the `node` argument other than for checking its type and deciding what to do based on the result. And since we always add `**kwargs` to the hooks' signatures, you can drop any parameter you don't use from the signature:

```python
import griffe
Expand Down Expand Up @@ -391,7 +393,7 @@ class MyExtension(griffe.Extension):

### Extra data

All Griffe objects (modules, classes, functions, attributes) can store additional (meta)data in their `extra` attribute. This attribute is a dictionary of dictionaries. The first layer is used as namespacing: each extension writes into its own namespace, or integrates with other projects by reading/writing in their namespaces, according to what they support and document.
All Griffe objects (modules, classes, functions, attributes, type aliases) can store additional (meta)data in their `extra` attribute. This attribute is a dictionary of dictionaries. The first layer is used as namespacing: each extension writes into its own namespace, or integrates with other projects by reading/writing in their namespaces, according to what they support and document.

```python
import griffe
Expand Down Expand Up @@ -563,10 +565,10 @@ See [how to use extensions](#using-extensions) to learn more about how to load a
> - [`Continue`][ast.Continue]
> - [`Del`][ast.Del]
> - [`Delete`][ast.Delete]
> - [`Dict`][ast.Dict]
>
> </td><td>
>
> - [`Dict`][ast.Dict]
> - [`DictComp`][ast.DictComp]
> - [`Div`][ast.Div]
> - `Ellipsis`[^1]
Expand Down Expand Up @@ -595,11 +597,11 @@ See [how to use extensions](#using-extensions) to learn more about how to load a
> - [`IsNot`][ast.IsNot]
> - [`JoinedStr`][ast.JoinedStr]
> - [`keyword`][ast.keyword]
> - [`Lambda`][ast.Lambda]
> - [`List`][ast.List]
>
> </td><td>
>
> - [`Lambda`][ast.Lambda]
> - [`List`][ast.List]
> - [`ListComp`][ast.ListComp]
> - [`Load`][ast.Load]
> - [`LShift`][ast.LShift]
Expand Down Expand Up @@ -627,11 +629,12 @@ See [how to use extensions](#using-extensions) to learn more about how to load a
> - [`NotEq`][ast.NotEq]
> - [`NotIn`][ast.NotIn]
> - `Num`[^1]
> - [`Or`][ast.Or]
> - [`ParamSpec`][ast.ParamSpec]
> - [`Pass`][ast.Pass]
>
> </td><td>
>
> - [`Or`][ast.Or]
> - [`Pass`][ast.Pass]
> - `pattern`[^3]
> - [`Pow`][ast.Pow]
> - `Print`[^4]
Expand All @@ -650,6 +653,9 @@ See [how to use extensions](#using-extensions) to learn more about how to load a
> - `TryExcept`[^5]
> - `TryFinally`[^6]
> - [`Tuple`][ast.Tuple]
> - [`TypeAlias`][ast.TypeAlias]
> - [`TypeVar`][ast.TypeVar]
> - [`TypeVarTuple`][ast.TypeVarTuple]
> - [`UAdd`][ast.UAdd]
> - [`UnaryOp`][ast.UnaryOp]
> - [`USub`][ast.USub]
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/users/how-to/support-decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class MyDecorator(griffe.Extension):
"""An extension to suport my decorator."""
```

Now we can declare the [`on_instance`][griffe.Extension.on_instance] hook, which receives any kind of Griffe object ([`Module`][griffe.Module], [`Class`][griffe.Class], [`Function`][griffe.Function], [`Attribute`][griffe.Attribute]), or we could use a kind-specific hook such as [`on_module_instance`][griffe.Extension.on_module_instance], [`on_class_instance`][griffe.Extension.on_class_instance], [`on_function_instance`][griffe.Extension.on_function_instance] and [`on_attribute_instance`][griffe.Extension.on_attribute_instance]. For example, if you know your decorator is only ever used on class declarations, it would make sense to use `on_class_instance`.
Now we can declare the [`on_instance`][griffe.Extension.on_instance] hook, which receives any kind of Griffe object ([`Module`][griffe.Module], [`Class`][griffe.Class], [`Function`][griffe.Function], [`Attribute`][griffe.Attribute], [`TypeAlias`][griffe.TypeAlias]), or we could use a kind-specific hook such as [`on_module_instance`][griffe.Extension.on_module_instance], [`on_class_instance`][griffe.Extension.on_class_instance], [`on_function_instance`][griffe.Extension.on_function_instance], [`on_attribute_instance`][griffe.Extension.on_attribute_instance] and [`on_type_alias_instance`][griffe.Extension.on_type_alias_instance]. For example, if you know your decorator is only ever used on class declarations, it would make sense to use `on_class_instance`.

For the example, lets use the `on_function_instance` hook, which receives `Function` instances.

Expand Down
17 changes: 12 additions & 5 deletions docs/guide/users/navigating.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Griffe loads API data into data models. These models provide various attributes
- [`Class`][griffe.Class], representing Python classes;
- [`Function`][griffe.Function], representing Python functions and class methods;
- [`Attribute`][griffe.Attribute], representing object attributes that weren't identified as modules, classes or functions;
- [`Type Alias`][griffe.TypeAlias], representing Python type aliases;
- [`Alias`][griffe.Alias], representing indirections such as imported objects or class members inherited from parent classes.

When [loading an object](loading.md), Griffe will give you back an instance of one of these models. A few examples:
Expand Down Expand Up @@ -84,7 +85,7 @@ To access an object's members, there are a few options:

In particular, Griffe extensions should always use `get_member` instead of the subscript syntax `[]`. The `get_member` method only looks into regular members, while the subscript syntax looks into inherited members too (for classes), which cannot be correctly computed until a package is fully loaded (which is generally not the case when an extension is running).

- In addition to this, models provide the [`attributes`][griffe.Object.attributes], [`functions`][griffe.Object.functions], [`classes`][griffe.Object.classes] or [`modules`][griffe.Object.modules] attributes, which return only members of the corresponding kind. These attributes are computed dynamically each time (they are Python properties).
- In addition to this, models provide the [`attributes`][griffe.Object.attributes], [`functions`][griffe.Object.functions], [`classes`][griffe.Object.classes], [`type_aliases`][griffe.Object.type_aliases] or [`modules`][griffe.Object.modules] attributes, which return only members of the corresponding kind. These attributes are computed dynamically each time (they are Python properties).

The same way members are accessed, they can also be set:

Expand Down Expand Up @@ -121,7 +122,7 @@ If a base class cannot be resolved during computation of inherited members, Grif

If you want to access all members at once (both declared and inherited), use the [`all_members`][griffe.Object.all_members] attribute. If you want to access only declared members, use the [`members`][griffe.Object] attribute.

Accessing the [`attributes`][griffe.Object.attributes], [`functions`][griffe.Object.functions], [`classes`][griffe.Object.classes] or [`modules`][griffe.Object.modules] attributes will trigger inheritance computation, so make sure to only access them once everything is loaded by Griffe. Don't try to access inherited members in extensions, while visiting or inspecting modules.
Accessing the [`attributes`][griffe.Object.attributes], [`functions`][griffe.Object.functions], [`classes`][griffe.Object.classes], [`type_aliases`][griffe.Object.type_aliases] or [`modules`][griffe.Object.modules] attributes will trigger inheritance computation, so make sure to only access them once everything is loaded by Griffe. Don't try to access inherited members in extensions, while visiting or inspecting modules.

#### Limitations

Expand Down Expand Up @@ -218,7 +219,7 @@ Aliases chains are never partially resolved: either they are resolved down to th

## Object kind

The kind of an object (module, class, function, attribute or alias) can be obtained in several ways.
The kind of an object (module, class, function, attribute, type alias or alias) can be obtained in several ways.

- With the [`kind`][griffe.Object.kind] attribute and the [`Kind`][griffe.Kind] enumeration: `obj.kind is Kind.MODULE`.

Expand All @@ -230,7 +231,7 @@ The kind of an object (module, class, function, attribute or alias) can be obtai

When given a set of kinds, the method returns true if the object is of one of the given kinds.

- With the [`is_module`][griffe.Object.is_module], [`is_class`][griffe.Object.is_class], [`is_function`][griffe.Object.is_function], [`is_attribute`][griffe.Object.is_attribute], and [`is_alias`][griffe.Object.is_alias] attributes.
- With the [`is_module`][griffe.Object.is_module], [`is_class`][griffe.Object.is_class], [`is_function`][griffe.Object.is_function], [`is_attribute`][griffe.Object.is_attribute], [`is_type_alias`][griffe.Object.is_type_alias], and [`is_alias`][griffe.Object.is_alias] attributes.

Additionally, it is possible to check if an object is a sub-kind of module, with the following attributes:

Expand Down Expand Up @@ -351,7 +352,7 @@ After a package is loaded, it is still possible to change the style used for spe

Do note, however, that the `parsed` attribute is cached, and won't be reset when overriding the `parser` or `parser_options` values.

Docstrings have a [`parent`][griffe.Docstring.parent] field too, that is a reference to their respective module, class, function or attribute.
Docstrings have a [`parent`][griffe.Docstring.parent] field too, that is a reference to their respective module, class, function, attribute or type alias.

## Model-specific fields

Expand All @@ -370,13 +371,15 @@ Models have most fields in common, but also have specific fields.
- [`overloads`][griffe.Class.overloads]: A dictionary to store overloads for class-level methods.
- [`decorators`][griffe.Class.decorators]: The [decorators][griffe.Decorator] applied to the class.
- [`parameters`][griffe.Class.parameters]: The [parameters][griffe.Parameters] of the class' `__init__` method, if any.
- [`type_parameters`][griffe.Class.type_parameters]: The [type parameters][griffe.TypeParameters] of the class.

### Functions

- [`decorators`][griffe.Function.decorators]: The [decorators][griffe.Decorator] applied to the function.
- [`overloads`][griffe.Function.overloads]: The overloaded signatures of the function.
- [`parameters`][griffe.Function.parameters]: The [parameters][griffe.Parameters] of the function.
- [`returns`][griffe.Function.returns]: The type annotation of the returned value, in the form of an [expression][griffe.Expr]. The `annotation` field can also be used, for compatibility with attributes.
- [`type_parameters`][griffe.Function.type_parameters]: The [type parameters][griffe.TypeParameters] of the function.

### Attributes

Expand All @@ -385,6 +388,10 @@ Models have most fields in common, but also have specific fields.
- [`deleter`][griffe.Attribute.deleter]: The property deleter.
- [`setter`][griffe.Attribute.setter]: The property setter.

### Type aliases
- [`value`][griffe.TypeAlias.value]: The value of the type alias, in the form of an [expression][griffe.Expr].
- [`type_parameters`][griffe.TypeAlias.type_parameters]: The [type parameters][griffe.TypeParameters] of the type alias.

### Alias

- [`alias_lineno`][griffe.Alias.alias_lineno]: The alias line number (where the object is imported).
Expand Down
22 changes: 19 additions & 3 deletions docs/guide/users/recommendations/docstrings.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ Here are explanations on what docstrings are, and a few recommendations on how t

## Definition

A docstring is a line or block of text describing objects such as modules, classes, functions and attributes. They are written below the object signature or assignment, or appear as first expression in a module:
A docstring is a line or block of text describing objects such as modules, classes, functions, attributes and type aliases. They are written below the object signature or assignment, or appear as first expression in a module:

```python title="module.py"
"""This is the module docstring."""

type X = dict[str, int]
"""This is a type alias docstring."""

a = 0
"""This is an attribute docstring."""

Expand Down Expand Up @@ -51,7 +54,7 @@ Whatever markup you choose, try to stay consistent within your code base.

## Styles

Docstrings can be written for modules, classes, functions, and attributes. But there are other aspects of a Python API that need to be documented, such as function parameters, returned values, and raised exceptions, to name a few. We could document everything in natural language, but that would make it hard for downstream tools such as documentation generators to extract information in a structured way, to allow dedicated rendering such as tables for parameters.
Docstrings can be written for modules, classes, functions, attributes, and type aliases. But there are other aspects of a Python API that need to be documented, such as function parameters, returned values, and raised exceptions, to name a few. We could document everything in natural language, but that would make it hard for downstream tools such as documentation generators to extract information in a structured way, to allow dedicated rendering such as tables for parameters.

To compensate for the lack of structure in natural languages, docstring "styles" emerged. A docstring style is a micro-format for docstrings, allowing to structure the information by following a specific format. With the most popular Google and Numpydoc styles, information in docstrings is decomposed into **sections** of different kinds, for example "parameter" sections or "return" sections. Some kinds of section then support documenting multiple items, or support a single block of markup. For example, we can document multiple parameters in "parameter" sections, but a "note" section is only composed of a text block.

Expand Down Expand Up @@ -136,7 +139,7 @@ When documenting objects acting as namespaces (modules, classes, enumerations),

## Modules

Module docstrings should briefly explain what the module contains, and for what purposes these objects can be used. If the documentation generator you chose does not support generating member summaries automatically, you might want to add docstrings sections for attributes, functions, classes and submodules.
Module docstrings should briefly explain what the module contains, and for what purposes these objects can be used. If the documentation generator you chose does not support generating member summaries automatically, you might want to add docstrings sections for attributes, functions, classes, type aliases and submodules.

```python title="package/__init__.py"
"""A generic package to demonstrate docstrings.
Expand Down Expand Up @@ -305,6 +308,19 @@ class GhostTown:
"""The town's size."""
```

## Type aliases

Type alias docstrings are written below their assignment. As usual, they should have a short summary, and an optional, longer body.

```python
type Callback = typing.Callable[[int, str], typing.Any]
"""Callback type for Frobnicators.

The first argument is the number of rounds to run, the second argument
is the name of the widget being frobnicated.
"""
```

## Exceptions, warnings

Callables that raise exceptions or emit warnings can document each of these exceptions and warnings. Documenting them informs your users that they could or should catch the raised exceptions, or that they could filter or configure warnings differently. The description next to each exception or warning should explain how or when they are raised or emitted.
Expand Down
Loading
Loading