Skip to content

Commit

Permalink
Slightly refined documentation while reading it (#1106)
Browse files Browse the repository at this point in the history
* Fixed typos in why.md

* Rephrased introductory sentences

* Update init.md

* Update docs/init.md

* Update docs/init.md

* Update docs/init.md

---------

Co-authored-by: Hynek Schlawack <hs@ox.cx>
  • Loading branch information
raratiru and hynek committed Mar 18, 2023
1 parent f7f0bf4 commit 5d9753a
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 11 deletions.
19 changes: 10 additions & 9 deletions docs/init.md
Expand Up @@ -213,10 +213,12 @@ Traceback (most recent call last):
ValueError: 'x' has to be smaller than 'y'!
```

This example also shows of some syntactic sugar for using the {obj}`attrs.validators.and_` validator: if you pass a list, all validators have to pass.
This example demonstrates a convenience shortcut:
Passing a list of validators directly is equivalent to passing them wrapped in the {obj}`attrs.validators.and_` validator and all validators must pass.

*attrs* won't intercept your changes to those attributes but you can always call {func}`attrs.validate` on any instance to verify that it's still valid:
When using {func}`attrs.define` or [`attrs.frozen`](attrs.frozen), *attrs* will run the validators even when setting the attribute.

When using {func}`attrs.define` or [`attrs.frozen`](attrs.frozen), however, *attrs* will run the validators even when setting the attribute.

```{doctest}
>>> i = C(4, 5)
Expand All @@ -241,7 +243,7 @@ TypeError: ("'x' must be <type 'int'> (got '42' that is a <type 'str'>).", Attri
```

Of course you can mix and match the two approaches at your convenience.
If you define validators both ways for an attribute, they are both ran:
If you use both ways to define validators for an attribute, they are both ran:

```{doctest}
>>> @define
Expand All @@ -263,7 +265,7 @@ Traceback (most recent call last):
ValueError: value out of bounds
```

And finally you can disable validators globally:
Finally, validators can be globally disabled:

```{doctest}
>>> attrs.validators.set_disabled(True)
Expand All @@ -276,7 +278,7 @@ Traceback (most recent call last):
TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", Attribute(name='x', default=NOTHING, validator=[<instance_of validator for type <class 'int'>>, <function fits_byte at 0x10fd7a0d0>], repr=True, cmp=True, hash=True, init=True, metadata=mappingproxy({}), type=None, converter=None), <class 'int'>, '128')
```

You can achieve the same by using the context manager:
... or within a context manager:

```{doctest}
>>> with attrs.validators.disabled():
Expand All @@ -292,8 +294,7 @@ TypeError: ("'x' must be <class 'int'> (got '128' that is a <class 'str'>).", At

## Converters

Finally, sometimes you may want to normalize the values coming in.
For that *attrs* comes with converters.
Sometimes, it is necessary to normalize the values coming in, therefore *attrs* comes with converters.

Attributes can have a `converter` function specified, which will be called with the attribute's passed-in value to get a new value to use.
This can be useful for doing type-conversions on values that you don't want to force your callers to do.
Expand Down Expand Up @@ -350,10 +351,10 @@ A converter will override an explicit type annotation or `type` argument.

## Hooking Yourself Into Initialization

Generally speaking, the moment you think that you need finer control over how your class is instantiated than what *attrs* offers, it's usually best to use a {obj}`classmethod` factory or to apply the [builder pattern](https://en.wikipedia.org/wiki/Builder_pattern).
Generally speaking, the moment you realize the need of finer control than what *attrs* offers – over how a class is instantiated, it's usually best to use a {obj}`classmethod` factory or to apply the [builder pattern](https://en.wikipedia.org/wiki/Builder_pattern).

However, sometimes you need to do that one quick thing before or after your class is initialized.
And for that *attrs* offers three means:
For that purpose, *attrs* offers the following options:

- `__attrs_pre_init__` is automatically detected and run *before* *attrs* starts initializing.
This is useful if you need to inject a call to `super().__init__()`.
Expand Down
4 changes: 2 additions & 2 deletions docs/why.md
Expand Up @@ -269,7 +269,7 @@ is roughly
ArtisanalClass(a=1, b=2)
```

which is quite a mouthful and it doesn't even use any of *attrs*'s more advanced features like validators or defaults values.
which is quite a mouthful and it doesn't even use any of *attrs*'s more advanced features like validators or default values.
Also: no tests whatsoever.
And who will guarantee you, that you don't accidentally flip the `<` in your tenth implementation of `__gt__`?

Expand All @@ -293,5 +293,5 @@ If you don't care and like typing, we're not gonna stop you.

However it takes a lot of bias and determined rationalization to claim that *attrs* raises the mental burden on a project given how difficult it is to find the important bits in a hand-written class and how annoying it is to ensure you've copy-pasted your code correctly over all your classes.

In any case, if you ever get sick of the repetitiveness and drowning important code in a sea of boilerplate, *attrs* will be waiting for you.
In any case, if you ever get sick of the repetitiveness and the drowning of important code in a sea of boilerplate, *attrs* will be waiting for you.
:::

0 comments on commit 5d9753a

Please sign in to comment.