Skip to content

Commit b27434e

Browse files
committed
damn
1 parent 3c24fd2 commit b27434e

16 files changed

Lines changed: 481 additions & 38 deletions

File tree

.github/workflows/docs.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: ci
2+
on:
3+
push:
4+
branches:
5+
- main
6+
permissions:
7+
contents: write
8+
jobs:
9+
deploy:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- name: Configure Git Credentials
14+
run: |
15+
git config user.name github-actions[bot]
16+
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
17+
- uses: actions/setup-python@v5
18+
with:
19+
python-version: 3.x
20+
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
21+
- uses: actions/cache@v4
22+
with:
23+
key: mkdocs-material-${{ env.cache_id }}
24+
path: .cache
25+
restore-keys: |
26+
mkdocs-material-
27+
- run: pip install mkdocs-material
28+
- run: mkdocs gh-deploy --force

docs/dumping.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Dumping
2+
3+
Dump the model to a data format. Supports nested items.
4+
5+
## JSON
6+
7+
Everyone's favorite data type.
8+
9+
```python
10+
from exacting import Exact
11+
12+
class Money(Exact):
13+
swag: bool
14+
15+
money = Money(swag=True)
16+
17+
json = money.exact_as_json()
18+
print(json) # {"swag": true}
19+
20+
data = Money.exact_from_json(json)
21+
print(data) # Money(swag=True)
22+
```
23+
24+
Side note, you can actually load from JSON with comments (jsonc). Just disable strict mode via `strict=False`.
25+
26+
```python
27+
json = """{
28+
"swag": /* false */ true, // yeah, trailing commas
29+
}"""
30+
31+
data = Money.exact_from_json(json, strict=False)
32+
print(data) # Money(swag=True)
33+
```
34+
35+
36+
## Bytes
37+
38+
Exacting uses [rkyv](https://docs.rs/rkyv/latest/rkyv/) for serialization/deserialization.
39+
40+
```python
41+
from exacting import Exact
42+
43+
class Place(Exact):
44+
name: str
45+
46+
place = Place(name="Freddy Fazbear's Pizza")
47+
archive = place.exact_as_bytes()
48+
print(archive) # b"\x00\x00\x00\x00name\xff..."
49+
50+
data = Place.exact_from_bytes(archive)
51+
print(data) # Place(name="Freddy Fazbear's Pizza")
52+
```

docs/fields.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Fields
2+
3+
Fields in `exacting` are rather pretty straightforward, just as what you'd expect from `dataclasses`.
4+
5+
```python
6+
from exacting import Exact, field
7+
8+
class StoreItem(Exact):
9+
name: str = field()
10+
price: int = field()
11+
```
12+
13+
## Min, max
14+
15+
You can check for the minimum/maximum length or value by passing the `minv` or `maxv` parameter.
16+
17+
```python
18+
from exacting import Exact, field
19+
20+
class Sequence(Exact):
21+
id: str = field(minv=2) # checks for len()
22+
some: int = field(maxv=4) # checks for value
23+
data: list[int] = field(minv=4, maxv=4) # len() must be 4
24+
25+
26+
# ✅ OK!
27+
Sequence(
28+
id="hello",
29+
some=4,
30+
data=[1, 2, 3, 4]
31+
)
32+
```
33+
34+
## Regex
35+
36+
`exacting` also has a built-in Regex matching field utility.
37+
38+
```python
39+
from exacting import Exact, field
40+
41+
class Hamburger(Exact):
42+
name: str = field(regex="^[A-Z]+$")
43+
44+
# ✅ GOOD. Emphasized enough!
45+
Hamburger(name="WHOPPER")
46+
47+
# ❌ ERROR. Can you be louder?
48+
Hamburger(name="bigmac")
49+
```
50+
51+
52+
??? failure "TypeError: Error while validating…"
53+
54+
```
55+
TypeError:
56+
Error while validating dataclass 'Hamburger' at attribute 'name' (field validator):
57+
Failed to validate Regex on str (doesn't match)
58+
```
59+
60+
Note that the `regex` parameter won't work (skipped) if used on field types that aren't `str`.
61+
62+
## Aliases
63+
64+
Aliases are only used when serializing/deserializing.
65+
66+
```python
67+
from exacting import Exact, field
68+
69+
class Person(Exact):
70+
name: str
71+
description: str = field(alias="desc")
72+
73+
# serializing
74+
person = Person(name="Me", description="broke af")
75+
data = person.exact_as_json()
76+
print(data) # {"name": "Me", "desc": "broke af"}
77+
78+
# deserializing
79+
person2 = Person.exact_from_json(data)
80+
print(person2) # Person(name='Me', description='broke af')
81+
```

docs/index.md

Lines changed: 101 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,106 @@
1-
# Welcome to MkDocs
1+
# Exacting
22

3-
For full documentation visit [mkdocs.org](https://www.mkdocs.org).
3+
> *(adj.) making great demands on one's skill, attention, or other resources.*
44
5-
## Commands
5+
`exacting` is a picky dataclass runtime utility collection, making sure all type annotations are followed.
66

7-
* `mkdocs new [dir-name]` - Create a new project.
8-
* `mkdocs serve` - Start the live-reloading docs server.
9-
* `mkdocs build` - Build the documentation site.
10-
* `mkdocs -h` - Print help message and exit.
7+
Essentially... **THE** go-to option for dataclasses. heh.
118

12-
## Project layout
9+
**🔑 Key features**:
10+
11+
- **100% static typing.** Because I hate nothing too.
12+
- Up to **10x faster** than [`pydantic`](https://pydantic.dev)! (Them: 60ms, us: 6~9ms)
13+
14+
![static typing](static-typing-proof.png)
15+
16+
<br />
17+
18+
**🛍️ Get `exacting`**:
19+
20+
=== "pip"
21+
22+
``` haskell
23+
pip install -U exacting
24+
```
25+
26+
=== "uv"
27+
28+
``` haskell
29+
uv pip install -U exacting
30+
```
31+
32+
=== "git"
33+
34+
``` shell
35+
git clone https://github.com/AWeirdDev/exacting
36+
```
37+
38+
**🔥 Define some model**:
39+
40+
=== "Python 3.10+"
41+
42+
```python
43+
from exacting import Exact
44+
45+
class Actor(Exact):
46+
name: str
47+
portrays: str
48+
49+
class Show(Exact):
50+
name: str
51+
description: str | None
52+
actors: list[Actor]
53+
```
54+
55+
=== "Python <= 3.9"
56+
57+
```python
58+
from typing import List, Optional
59+
from exacting import Exact
60+
61+
class Actor(Exact):
62+
name: str
63+
portrays: str
64+
65+
class Show(Exact):
66+
name: str
67+
description: Optional[str]
68+
actors: List[Actor]
69+
```
70+
71+
**📦 Build 'em**:
72+
73+
```python
74+
# (1) ✅ OK, exacting is happi
75+
Show(
76+
name="Severance",
77+
description="great show",
78+
actors=[
79+
Actor(name="Adam Scott", portrays="Mark S."),
80+
Actor(name="Britt Lower", portrays="Helly R."),
81+
]
82+
)
83+
84+
# (2) ❌ Nuh-uh, exacting is angri
85+
Show(
86+
name=123,
87+
description=False,
88+
actors=[
89+
"Walter White",
90+
"Jesse Pinkman"
91+
]
92+
)
93+
```
94+
95+
??? failure "TypeError: Error while validating…"
96+
97+
``` python
98+
TypeError:
99+
Error while validating dataclass 'Show' at attribute 'name':
100+
(isinstance) Expected <class 'str'>, got: <class 'int'>
101+
```
102+
103+
Normally, when you use the parameters passed in example (2) above, the Python `dataclasses` library might as well just go with it, because they only put the additional **static typing** to the model, but not at **runtime**. Exacting makes sure that at both times, types are all enforced. It even gives you a detailed error message on where this occurs! (In a cool way)
104+
105+
It's worth noting that error generations are *lazy*, which means once Exacting finds out about a problem, it immediately raises a `TypeError` or `ValueError` (depending on the context) when we're at the high-level scale (say, a dataclass). This saves a lot of computation time if you have a larger model.
13106

14-
mkdocs.yml # The configuration file.
15-
docs/
16-
index.md # The documentation homepage.
17-
... # Other markdown pages, images and other files.

docs/static-typing-proof.png

15.4 KB
Loading

docs/type-reference/annotated.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Annotated
2+
3+
Annotated types allow you to annotate any value onto the type field.
4+
5+
```python
6+
from typing import Annotated
7+
8+
# type, metadata.......
9+
Annotated[str, "any data", 123]
10+
```
11+
12+
`exacting` only checks for the type, not the metadata.
13+
14+
For future development of `exacting`, it might be used as a doc field.

docs/type-reference/any.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Any
2+
3+
The `Any` type allows any type to be on the surface, which is generally not a good practice in production.
4+
5+
```python
6+
from typing import Any
7+
8+
Any
9+
```
10+

docs/type-reference/literal.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Literal
2+
3+
Literal types tell `exacting` to check for value equality instead of types.
4+
5+
```python
6+
from typing import Literal
7+
8+
Literal["Hello", b"beep", 123, True]
9+
```
10+
11+
There can be multiple `Literal` items.
12+
13+
Literal types perform exactly as intended in `exacting`.

docs/type-reference/native.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Native types
2+
3+
Most native types are instantly available! These are all the available types:
4+
5+
```python
6+
str
7+
int
8+
float
9+
bool
10+
bytes
11+
list[...]
12+
dict[..., ...]
13+
```
14+
15+
You probably noticed that `tuple` or `set` isn't available. Yes, currently.
16+

docs/type-reference/union.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Union
2+
3+
The union type allows you to have multiple choices of types.
4+
5+
=== "Python 3.10+"
6+
7+
```python
8+
A | B | C
9+
```
10+
11+
=== "Python <= 3.9"
12+
13+
```python
14+
from typing import Union
15+
16+
Union[A, B, C]
17+
```
18+
19+
Union types perform exactly as intended in `exacting`.

0 commit comments

Comments
 (0)