Skip to content
This repository was archived by the owner on Aug 19, 2025. It is now read-only.

Commit 851b60f

Browse files
Version 0.3.0 (#98)
Co-authored-by: Amin Alaee <[email protected]>
1 parent 1027238 commit 851b60f

31 files changed

+944
-951
lines changed

docs/examples.md

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,21 @@ uvicorn
1515
**app.py**
1616

1717
```python
18+
import typesystem
19+
import uvicorn
1820
from starlette.applications import Starlette
1921
from starlette.responses import JSONResponse
2022
from starlette.routing import Route
21-
import typesystem
22-
import uvicorn
2323

2424
users = []
2525

2626

27-
class User(typesystem.Schema):
28-
username = typesystem.String(max_length=100)
29-
is_admin = typesystem.Boolean(default=False)
27+
user_schema = typesystem.Schema(
28+
fields={
29+
"username": typesystem.String(max_length=100),
30+
"is_admin": typesystem.Boolean(default=False),
31+
}
32+
)
3033

3134

3235
async def list_users(request):
@@ -39,7 +42,7 @@ async def add_user(request):
3942
if errors:
4043
return JSONResponse(dict(errors), status_code=400)
4144
users.append(user)
42-
return JSONResponse(dict(user))
45+
return JSONResponse(user)
4346

4447

4548
app = Starlette(debug=True, routes=[
@@ -73,56 +76,55 @@ uvicorn
7376
**app.py**
7477

7578
```python
79+
import typesystem
80+
import uvicorn
7681
from starlette.applications import Starlette
7782
from starlette.responses import RedirectResponse
78-
from starlette.routing import Route, Mount
83+
from starlette.routing import Mount, Route
7984
from starlette.staticfiles import StaticFiles
8085
from starlette.templating import Jinja2Templates
81-
import typesystem
82-
import uvicorn
8386

8487
forms = typesystem.Jinja2Forms(package="bootstrap4")
8588
templates = Jinja2Templates(directory="templates")
8689
statics = StaticFiles(directory="statics", packages=["bootstrap4"])
8790
bookings = []
8891

8992

90-
class BookingSchema(typesystem.Schema):
91-
start_date = typesystem.Date(title="Start date")
92-
end_date = typesystem.Date(title="End date")
93-
room = typesystem.Choice(
94-
title="Room type",
95-
choices=[
96-
("double", "Double room"),
97-
("twin", "Twin room"),
98-
("single", "Single room"),
99-
],
100-
)
101-
include_breakfast = typesystem.Boolean(title="Include breakfast", default=False)
102-
103-
def __str__(self):
104-
breakfast = (
105-
"(with breakfast)" if self.include_breakfast else "(without breakfast)"
106-
)
107-
return f"Booking for {self.room} from {self.start_date} to {self.end_date}"
93+
booking_schema = typesystem.Schema(
94+
fields={
95+
"start_date": typesystem.Date(title="Start date"),
96+
"end_date": typesystem.Date(title="End date"),
97+
"room": typesystem.Choice(
98+
title="Room type",
99+
choices=[
100+
("double", "Double room"),
101+
("twin", "Twin room"),
102+
("single", "Single room"),
103+
],
104+
),
105+
"include_breakfast": typesystem.Boolean(
106+
title="Include breakfast", default=False
107+
),
108+
}
109+
)
108110

109111

110112
async def homepage(request):
111-
form = forms.Form(BookingSchema)
113+
form = forms.create_form(booking_schema)
112114
context = {"request": request, "form": form, "bookings": bookings}
113115
return templates.TemplateResponse("index.html", context)
114116

115117

116118
async def make_booking(request):
117119
data = await request.form()
118-
booking, errors = BookingSchema.validate_or_error(data)
120+
booking, errors = booking_schema.validate_or_error(data)
119121
if errors:
120-
form = forms.Form(BookingSchema, values=data, errors=errors)
122+
form = forms.create_form(booking_schema)
121123
context = {"request": request, "form": form, "bookings": bookings}
122124
return templates.TemplateResponse("index.html", context)
123125

124126
bookings.append(booking)
125-
return RedirectResponse(request.url_for("homepage"))
127+
return RedirectResponse(request.url_for("homepage"), status_code=303)
126128

127129

128130
app = Starlette(

docs/fields.md

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1-
Fields are usually declared as attributes on schema classes:
1+
Fields are passed as a dictionary to the Schema classes:
22

33
```python
4-
class Organisation(typesystem.Schema):
5-
name = typesystem.String(title="Name", max_length=100)
6-
date_created = typesystem.Date(title="Date created", default=datetime.date.today)
7-
owner = typesystem.Reference(to=User, allow_null=True)
4+
import typesystem
5+
6+
user_schema = typesystem.Schema(fields={"name": typesystem.String()})
7+
8+
definitions = typesystem.Definitions()
9+
definitions["User"] = user_schema
10+
11+
organization_schema = typesystem.Schema(
12+
fields={
13+
"name": typesystem.String(title="Name", max_length=100),
14+
"date_created": typesystem.Date(title="Date created", default=datetime.date.today),
15+
"owner": typesystem.Reference(to="User", allow_null=True, definitions=definitions),
16+
}
17+
)
818
```
919

1020
Fields are always *required* in inputs, unless a *default* value is set.
@@ -20,6 +30,7 @@ All fields support the following arguments.
2030
* `description` - A string describing the input. **Default: `None`**
2131
* `default` - A value to be used if no input is provided for this field. May be a callable, such as `datetime.datetime.now`. **Default: `NO_DEFAULT`**
2232
* `allow_null` - A boolean determining if `None` values are valid. **Default: `False`**
33+
* `read_only` - A boolean determining if field should be considered as read-only, this is usually used in form rendering. **Default: `False`**
2334

2435
## Using fields directly
2536

@@ -60,6 +71,7 @@ For example: `username = typesystem.String(max_length=100)`
6071
* `min_length` - A minimum number of characters that valid input stings may contain. **Default: `None`**
6172
* `pattern` - A regular expression that must match. This can be either a string or a compiled regular expression. E.g. `pattern="^[A-Za-z]+$"` **Default: `None`**
6273
* `format` - A string used to indicate a semantic type, such as `"email"`, `"url"`, or `"color"`. **Default: `None`**
74+
* `coerce_types` - A boolean determining if type casting should be done, E.g. changing `None` to `""` if `allow_blank`. **Default: `True`**
6375

6476
### Text
6577

@@ -181,7 +193,7 @@ extra_metadata = typesystem.Object(properties=typesystem.String(max_length=100))
181193
Schema classes implement their validation behaviour by generating an `Object`
182194
field, and automatically determining the `properties` and `required` attributes.
183195

184-
You'll typically want to use `typesystem.Reference(to=SomeSchema)` rather than
196+
You'll typically want to use `typesystem.Reference(to="SomeSchema")` rather than
185197
using the `Object` field directly, but it can be useful if you have a more
186198
complex data structure that you need to validate.
187199

@@ -201,12 +213,13 @@ Used to reference a nested schema.
201213
For example:
202214

203215
```python
204-
owner = typesystem.Reference(to=User, allow_null=True)
216+
owner = typesystem.Reference(to="User", allow_null=True, definitions=definitions)
205217
```
206218

207219
**Arguments**:
208220

209-
* `to` - A schema class or field instance. **Required**
221+
* `to` - Name of schema defined in definitions. **Required**
222+
* `definitions` - `Definitions` instance. **Required**
210223

211224
## Other data types
212225

docs/forms.md

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,21 @@ import typesystem
88

99
forms = typesystem.Jinja2Forms(package="typesystem") # Use the default templates.
1010

11-
class BookingSchema(typesystem.Schema):
12-
start_date = typesystem.Date(title="Start date")
13-
end_date = typesystem.Date(title="End date")
14-
room = typesystem.Choice(title="Room type", choices=[
15-
('double', 'Double room'),
16-
('twin', 'Twin room'),
17-
('single', 'Single room')
18-
])
19-
include_breakfast = typesystem.Boolean(title="Include breakfast", default=False)
20-
21-
form = forms.Form(BookingSchema)
11+
booking_schema = typesystem.Schema(
12+
fields={
13+
"start_date": typesystem.Date(title="Start date"),
14+
"end_date": typesystem.Date(title="End date"),
15+
"room": typesystem.Choice(title="Room type", choices=[
16+
('double', 'Double room'),
17+
('twin', 'Twin room'),
18+
('single', 'Single room')
19+
]),
20+
"include_breakfast": typesystem.Boolean(title="Include breakfast", default=False),
21+
22+
}
23+
)
24+
25+
form = forms.create_form(booking_schema)
2226
print(form)
2327
```
2428

@@ -34,26 +38,26 @@ Notice that only the fields in the form are rendered. The surrounding `<form>`,
3438
```html
3539
<tr>
3640
<td>
37-
<label for="form-bookingschema-start-date">Start date</label>
41+
<label for="start-date">Start date</label>
3842
</td>
3943
<td>
40-
<input type="date" id="form-bookingschema-start-date" name="start_date" required>
44+
<input type="date" id="start-date" name="start_date" required >
4145
</td>
4246
</tr>
4347
<tr>
4448
<td>
45-
<label for="form-bookingschema-end-date">End date</label>
49+
<label for="end-date">End date</label>
4650
</td>
4751
<td>
48-
<input type="date" id="form-bookingschema-end-date" name="end_date" required>
52+
<input type="date" id="end-date" name="end_date" required >
4953
</td>
5054
</tr>
5155
<tr>
5256
<td>
53-
<label for="form-bookingschema-room">Room type</label>
57+
<label for="room">Room type</label>
5458
</td>
5559
<td>
56-
<select id="form-bookingschema-room" name="room">
60+
<select id="room" name="room">
5761
<option></option>
5862
<option value="double">Double room</option>
5963
<option value="twin">Twin room</option>
@@ -63,10 +67,10 @@ Notice that only the fields in the form are rendered. The surrounding `<form>`,
6367
</tr>
6468
<tr>
6569
<td>
66-
<label for="form-bookingschema-include-breakfast">Include breakfast</label>
70+
<label for="include-breakfast">Include breakfast</label>
6771
</td>
6872
<td>
69-
<input type="checkbox" id="form-bookingschema-include-breakfast" name="include_breakfast" value="true">
73+
<input type="checkbox" id="include-breakfast" name="include_breakfast" value="true" >
7074
</td>
7175
</tr>
7276
```
@@ -92,15 +96,7 @@ We can include values in a form like so:
9296

9397
```python
9498
initial_values = {'room': 'double', 'include_breakfast': True}
95-
form = forms.Form(BookingSchema, values=initial_values)
96-
```
97-
98-
We can also include validation errors:
99-
100-
```python
101-
booking, errors = BookingSchema.validate_or_error(data)
102-
if errors:
103-
form = forms.Form(BookingSchema, values=data, errors=errors)
99+
form = forms.create_form(booking_schema, values=initial_values)
104100
```
105101

106102
## Customizing field rendering

docs/index.md

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,30 +50,30 @@ $ pip3 install typesystem[pyyaml]
5050
```python
5151
import typesystem
5252

53-
class Artist(typesystem.Schema):
54-
name = typesystem.String(max_length=100)
55-
56-
class Album(typesystem.Schema):
57-
title = typesystem.String(max_length=100)
58-
release_date = typesystem.Date()
59-
artist = typesystem.Reference(Artist)
60-
61-
album = Album.validate({
53+
artist_schema = typesystem.Schema(
54+
fields={
55+
"name": typesystem.String(max_length=100)
56+
}
57+
)
58+
59+
definitions = typesystem.Definitions()
60+
definitions["Artist"] = artist_schema
61+
62+
album_schema = typesystem.Schema(
63+
fields={
64+
"title": typesystem.String(max_length=100),
65+
"release_date": typesystem.Date(),
66+
"artist": typesystem.Reference("Artist", definitions=definitions)
67+
}
68+
)
69+
70+
album = album_schema.validate({
6271
"title": "Double Negative",
6372
"release_date": "2018-09-14",
6473
"artist": {"name": "Low"}
6574
})
6675

6776
print(album)
68-
# Album(title='Double Negative', release_date=datetime.date(2018, 9, 14), artist=Artist(name='Low'))
69-
70-
print(album.release_date)
71-
# datetime.date(2018, 9, 14)
72-
73-
print(album['release_date'])
74-
# '2018-09-14'
75-
76-
print(dict(album))
7777
# {'title': 'Double Negative', 'release_date': '2018-09-14', 'artist': {'name': 'Low'}}
7878
```
7979

@@ -82,7 +82,7 @@ print(dict(album))
8282
There are plenty of other great validation libraries for Python out there,
8383
including [Marshmallow](https://github.com/marshmallow-code/marshmallow),
8484
[Schematics](https://github.com/schematics/schematics),
85-
[Voluptuous](https://github.com/alecthomas/voluptuous), and many others.
85+
[Voluptuous](https://github.com/alecthomas/voluptuous), [Pydantic](https://github.com/samuelcolvin/pydantic/) and many others.
8686

8787
TypeSystem exists because I want a data validation library that offers
8888
first-class support for:

docs/json_schema.md

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
TypeSystem can convert Schema classes or Field instances to/from JSON Schema.
1+
TypeSystem can convert Schema or Field instances to/from JSON Schema.
22

33
!!! note
44
TypeSystem only supports `$ref` pointers that use the standard "definitions"
@@ -15,20 +15,21 @@ Let's define a schema, and dump it out into a JSON schema document:
1515
import json
1616
import typesystem
1717

18-
class BookingSchema(typesystem.Schema):
19-
start_date = typesystem.Date(title="Start date")
20-
end_date = typesystem.Date(title="End date")
21-
room = typesystem.Choice(
22-
title="Room type",
23-
choices=[
24-
("double", "Double room"),
25-
("twin", "Twin room"),
26-
("single", "Single room"),
27-
],
28-
)
29-
include_breakfast = typesystem.Boolean(title="Include breakfast", default=False)
18+
booking_schema = typesystem.Schema(
19+
fields={
20+
"start_date": typesystem.Date(title="Start date"),
21+
"end_date": typesystem.Date(title="End date"),
22+
"room": typesystem.Choice(title="Room type", choices=[
23+
('double', 'Double room'),
24+
('twin', 'Twin room'),
25+
('single', 'Single room')
26+
]),
27+
"include_breakfast": typesystem.Boolean(title="Include breakfast", default=False),
3028

31-
schema = typesystem.to_json_schema(BookingSchema)
29+
}
30+
)
31+
32+
schema = typesystem.to_json_schema(booking_schema)
3233
print(json.dumps(schema, indent=4))
3334
```
3435

0 commit comments

Comments
 (0)