prefect-client 3.2.2__py3-none-any.whl → 3.2.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. prefect/__init__.py +15 -8
  2. prefect/_build_info.py +5 -0
  3. prefect/client/orchestration/__init__.py +16 -5
  4. prefect/main.py +0 -2
  5. prefect/server/api/__init__.py +34 -0
  6. prefect/server/api/admin.py +85 -0
  7. prefect/server/api/artifacts.py +224 -0
  8. prefect/server/api/automations.py +239 -0
  9. prefect/server/api/block_capabilities.py +25 -0
  10. prefect/server/api/block_documents.py +164 -0
  11. prefect/server/api/block_schemas.py +153 -0
  12. prefect/server/api/block_types.py +211 -0
  13. prefect/server/api/clients.py +246 -0
  14. prefect/server/api/collections.py +75 -0
  15. prefect/server/api/concurrency_limits.py +286 -0
  16. prefect/server/api/concurrency_limits_v2.py +269 -0
  17. prefect/server/api/csrf_token.py +38 -0
  18. prefect/server/api/dependencies.py +196 -0
  19. prefect/server/api/deployments.py +941 -0
  20. prefect/server/api/events.py +300 -0
  21. prefect/server/api/flow_run_notification_policies.py +120 -0
  22. prefect/server/api/flow_run_states.py +52 -0
  23. prefect/server/api/flow_runs.py +867 -0
  24. prefect/server/api/flows.py +210 -0
  25. prefect/server/api/logs.py +43 -0
  26. prefect/server/api/middleware.py +73 -0
  27. prefect/server/api/root.py +35 -0
  28. prefect/server/api/run_history.py +170 -0
  29. prefect/server/api/saved_searches.py +99 -0
  30. prefect/server/api/server.py +891 -0
  31. prefect/server/api/task_run_states.py +52 -0
  32. prefect/server/api/task_runs.py +342 -0
  33. prefect/server/api/task_workers.py +31 -0
  34. prefect/server/api/templates.py +35 -0
  35. prefect/server/api/ui/__init__.py +3 -0
  36. prefect/server/api/ui/flow_runs.py +128 -0
  37. prefect/server/api/ui/flows.py +173 -0
  38. prefect/server/api/ui/schemas.py +63 -0
  39. prefect/server/api/ui/task_runs.py +175 -0
  40. prefect/server/api/validation.py +382 -0
  41. prefect/server/api/variables.py +181 -0
  42. prefect/server/api/work_queues.py +230 -0
  43. prefect/server/api/workers.py +656 -0
  44. prefect/settings/sources.py +18 -5
  45. {prefect_client-3.2.2.dist-info → prefect_client-3.2.4.dist-info}/METADATA +10 -15
  46. {prefect_client-3.2.2.dist-info → prefect_client-3.2.4.dist-info}/RECORD +48 -10
  47. {prefect_client-3.2.2.dist-info → prefect_client-3.2.4.dist-info}/WHEEL +1 -2
  48. prefect/_version.py +0 -21
  49. prefect_client-3.2.2.dist-info/top_level.txt +0 -1
  50. {prefect_client-3.2.2.dist-info → prefect_client-3.2.4.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,25 @@
1
+ """
2
+ Routes for interacting with block capabilities.
3
+ """
4
+
5
+ from typing import List
6
+
7
+ from fastapi import Depends
8
+
9
+ from prefect.server import models
10
+ from prefect.server.database import PrefectDBInterface, provide_database_interface
11
+ from prefect.server.utilities.server import PrefectRouter
12
+
13
+ router: PrefectRouter = PrefectRouter(
14
+ prefix="/block_capabilities", tags=["Block capabilities"]
15
+ )
16
+
17
+
18
+ @router.get("/")
19
+ async def read_available_block_capabilities(
20
+ db: PrefectDBInterface = Depends(provide_database_interface),
21
+ ) -> List[str]:
22
+ async with db.session_context() as session:
23
+ return await models.block_schemas.read_available_block_capabilities(
24
+ session=session
25
+ )
@@ -0,0 +1,164 @@
1
+ """
2
+ Routes for interacting with block objects.
3
+ """
4
+
5
+ from typing import List, Optional
6
+ from uuid import UUID
7
+
8
+ from fastapi import Body, Depends, HTTPException, Path, Query, status
9
+
10
+ from prefect.server import models, schemas
11
+ from prefect.server.api import dependencies
12
+ from prefect.server.database import PrefectDBInterface, provide_database_interface
13
+ from prefect.server.utilities.server import PrefectRouter
14
+
15
+ router: PrefectRouter = PrefectRouter(
16
+ prefix="/block_documents", tags=["Block documents"]
17
+ )
18
+
19
+
20
+ @router.post("/", status_code=status.HTTP_201_CREATED)
21
+ async def create_block_document(
22
+ block_document: schemas.actions.BlockDocumentCreate,
23
+ db: PrefectDBInterface = Depends(provide_database_interface),
24
+ ) -> schemas.core.BlockDocument:
25
+ """
26
+ Create a new block document.
27
+ """
28
+ async with db.session_context(begin_transaction=True) as session:
29
+ if block_document.name is not None:
30
+ exists = (
31
+ await models.block_documents.block_document_with_unique_values_exists(
32
+ session=session,
33
+ block_type_id=block_document.block_type_id,
34
+ name=block_document.name,
35
+ )
36
+ )
37
+ if exists:
38
+ raise HTTPException(
39
+ status.HTTP_409_CONFLICT,
40
+ detail="Block already exists",
41
+ )
42
+
43
+ return await models.block_documents.create_block_document(
44
+ session=session, block_document=block_document
45
+ )
46
+
47
+
48
+ @router.post("/filter")
49
+ async def read_block_documents(
50
+ limit: int = dependencies.LimitBody(),
51
+ block_documents: Optional[schemas.filters.BlockDocumentFilter] = None,
52
+ block_types: Optional[schemas.filters.BlockTypeFilter] = None,
53
+ block_schemas: Optional[schemas.filters.BlockSchemaFilter] = None,
54
+ include_secrets: bool = Body(
55
+ False, description="Whether to include sensitive values in the block document."
56
+ ),
57
+ sort: Optional[schemas.sorting.BlockDocumentSort] = Body(
58
+ schemas.sorting.BlockDocumentSort.NAME_ASC
59
+ ),
60
+ offset: int = Body(0, ge=0),
61
+ db: PrefectDBInterface = Depends(provide_database_interface),
62
+ ) -> List[schemas.core.BlockDocument]:
63
+ """
64
+ Query for block documents.
65
+ """
66
+ async with db.session_context() as session:
67
+ result = await models.block_documents.read_block_documents(
68
+ session=session,
69
+ block_document_filter=block_documents,
70
+ block_type_filter=block_types,
71
+ block_schema_filter=block_schemas,
72
+ include_secrets=include_secrets,
73
+ sort=sort,
74
+ offset=offset,
75
+ limit=limit,
76
+ )
77
+
78
+ return result
79
+
80
+
81
+ @router.post("/count")
82
+ async def count_block_documents(
83
+ block_documents: Optional[schemas.filters.BlockDocumentFilter] = None,
84
+ block_types: Optional[schemas.filters.BlockTypeFilter] = None,
85
+ block_schemas: Optional[schemas.filters.BlockSchemaFilter] = None,
86
+ db: PrefectDBInterface = Depends(provide_database_interface),
87
+ ) -> int:
88
+ """
89
+ Count block documents.
90
+ """
91
+ async with db.session_context() as session:
92
+ result = await models.block_documents.count_block_documents(
93
+ session=session,
94
+ block_document_filter=block_documents,
95
+ block_type_filter=block_types,
96
+ block_schema_filter=block_schemas,
97
+ )
98
+
99
+ return result
100
+
101
+
102
+ @router.get("/{id:uuid}")
103
+ async def read_block_document_by_id(
104
+ block_document_id: UUID = Path(
105
+ ..., description="The block document id", alias="id"
106
+ ),
107
+ include_secrets: bool = Query(
108
+ False, description="Whether to include sensitive values in the block document."
109
+ ),
110
+ db: PrefectDBInterface = Depends(provide_database_interface),
111
+ ) -> schemas.core.BlockDocument:
112
+ async with db.session_context() as session:
113
+ block_document = await models.block_documents.read_block_document_by_id(
114
+ session=session,
115
+ block_document_id=block_document_id,
116
+ include_secrets=include_secrets,
117
+ )
118
+ if not block_document:
119
+ raise HTTPException(status.HTTP_404_NOT_FOUND, "Block document not found")
120
+ return block_document
121
+
122
+
123
+ @router.delete("/{id:uuid}", status_code=status.HTTP_204_NO_CONTENT)
124
+ async def delete_block_document(
125
+ block_document_id: UUID = Path(
126
+ ..., description="The block document id", alias="id"
127
+ ),
128
+ db: PrefectDBInterface = Depends(provide_database_interface),
129
+ ) -> None:
130
+ async with db.session_context(begin_transaction=True) as session:
131
+ result = await models.block_documents.delete_block_document(
132
+ session=session, block_document_id=block_document_id
133
+ )
134
+ if not result:
135
+ raise HTTPException(
136
+ status.HTTP_404_NOT_FOUND, detail="Block document not found"
137
+ )
138
+
139
+
140
+ @router.patch("/{id:uuid}", status_code=status.HTTP_204_NO_CONTENT)
141
+ async def update_block_document_data(
142
+ block_document: schemas.actions.BlockDocumentUpdate,
143
+ block_document_id: UUID = Path(
144
+ ..., description="The block document id", alias="id"
145
+ ),
146
+ db: PrefectDBInterface = Depends(provide_database_interface),
147
+ ) -> None:
148
+ try:
149
+ async with db.session_context(begin_transaction=True) as session:
150
+ result = await models.block_documents.update_block_document(
151
+ session=session,
152
+ block_document_id=block_document_id,
153
+ block_document=block_document,
154
+ )
155
+ except ValueError as exc:
156
+ raise HTTPException(
157
+ status_code=status.HTTP_400_BAD_REQUEST,
158
+ detail=str(exc),
159
+ )
160
+
161
+ if not result:
162
+ raise HTTPException(
163
+ status.HTTP_404_NOT_FOUND, detail="Block document not found"
164
+ )
@@ -0,0 +1,153 @@
1
+ """
2
+ Routes for interacting with block schema objects.
3
+ """
4
+
5
+ from typing import List, Optional
6
+ from uuid import UUID
7
+
8
+ from fastapi import (
9
+ Body,
10
+ Depends,
11
+ HTTPException,
12
+ Path,
13
+ Query,
14
+ Response,
15
+ status,
16
+ )
17
+
18
+ from prefect.server import models, schemas
19
+ from prefect.server.api import dependencies
20
+ from prefect.server.database import PrefectDBInterface, provide_database_interface
21
+ from prefect.server.models.block_schemas import MissingBlockTypeException
22
+ from prefect.server.utilities.server import PrefectRouter
23
+
24
+ router: PrefectRouter = PrefectRouter(prefix="/block_schemas", tags=["Block schemas"])
25
+
26
+
27
+ @router.post("/", status_code=status.HTTP_201_CREATED)
28
+ async def create_block_schema(
29
+ block_schema: schemas.actions.BlockSchemaCreate,
30
+ response: Response,
31
+ db: PrefectDBInterface = Depends(provide_database_interface),
32
+ ) -> schemas.core.BlockSchema:
33
+ from prefect.blocks.core import Block
34
+
35
+ async with db.session_context(begin_transaction=True) as session:
36
+ block_type = await models.block_types.read_block_type(
37
+ session=session, block_type_id=block_schema.block_type_id
38
+ )
39
+ if block_type is None:
40
+ raise HTTPException(
41
+ status.HTTP_404_NOT_FOUND,
42
+ detail=f"Block type {block_schema.block_type_id} not found.",
43
+ )
44
+
45
+ block_schema_checksum = Block._calculate_schema_checksum(block_schema.fields)
46
+ existing_block_schema = (
47
+ await models.block_schemas.read_block_schema_by_checksum(
48
+ session=session,
49
+ checksum=block_schema_checksum,
50
+ version=block_schema.version,
51
+ )
52
+ )
53
+ if existing_block_schema:
54
+ response.status_code = status.HTTP_200_OK
55
+ return existing_block_schema
56
+ try:
57
+ model = await models.block_schemas.create_block_schema(
58
+ session=session,
59
+ block_schema=block_schema,
60
+ )
61
+ except MissingBlockTypeException as ex:
62
+ raise HTTPException(status.HTTP_409_CONFLICT, detail=str(ex))
63
+
64
+ return model
65
+
66
+
67
+ @router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT)
68
+ async def delete_block_schema(
69
+ block_schema_id: UUID = Path(..., description="The block schema id", alias="id"),
70
+ db: PrefectDBInterface = Depends(provide_database_interface),
71
+ api_version: str = Depends(dependencies.provide_request_api_version),
72
+ ) -> None:
73
+ """
74
+ Delete a block schema by id.
75
+ """
76
+ async with db.session_context(begin_transaction=True) as session:
77
+ block_schema = await models.block_schemas.read_block_schema(
78
+ session=session, block_schema_id=block_schema_id
79
+ )
80
+ if not block_schema:
81
+ raise HTTPException(
82
+ status_code=status.HTTP_404_NOT_FOUND, detail="Block schema not found"
83
+ )
84
+
85
+ if block_schema.block_type.is_protected:
86
+ raise HTTPException(
87
+ status.HTTP_403_FORBIDDEN,
88
+ detail="Block schemas for protected block types cannot be deleted.",
89
+ )
90
+
91
+ await models.block_schemas.delete_block_schema(
92
+ session=session, block_schema_id=block_schema_id
93
+ )
94
+
95
+
96
+ @router.post("/filter")
97
+ async def read_block_schemas(
98
+ block_schemas: Optional[schemas.filters.BlockSchemaFilter] = None,
99
+ limit: int = dependencies.LimitBody(),
100
+ offset: int = Body(0, ge=0),
101
+ db: PrefectDBInterface = Depends(provide_database_interface),
102
+ ) -> List[schemas.core.BlockSchema]:
103
+ """
104
+ Read all block schemas, optionally filtered by type
105
+ """
106
+ async with db.session_context() as session:
107
+ result = await models.block_schemas.read_block_schemas(
108
+ session=session,
109
+ block_schema_filter=block_schemas,
110
+ limit=limit,
111
+ offset=offset,
112
+ )
113
+ return result
114
+
115
+
116
+ @router.get("/{id}")
117
+ async def read_block_schema_by_id(
118
+ block_schema_id: UUID = Path(..., description="The block schema id", alias="id"),
119
+ db: PrefectDBInterface = Depends(provide_database_interface),
120
+ ) -> schemas.core.BlockSchema:
121
+ """
122
+ Get a block schema by id.
123
+ """
124
+ async with db.session_context() as session:
125
+ block_schema = await models.block_schemas.read_block_schema(
126
+ session=session, block_schema_id=block_schema_id
127
+ )
128
+ if not block_schema:
129
+ raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Block schema not found")
130
+ return block_schema
131
+
132
+
133
+ @router.get("/checksum/{checksum}")
134
+ async def read_block_schema_by_checksum(
135
+ block_schema_checksum: str = Path(
136
+ ..., description="The block schema checksum", alias="checksum"
137
+ ),
138
+ db: PrefectDBInterface = Depends(provide_database_interface),
139
+ version: Optional[str] = Query(
140
+ None,
141
+ description=(
142
+ "Version of block schema. If not provided the most recently created block"
143
+ " schema with the matching checksum will be returned."
144
+ ),
145
+ ),
146
+ ) -> schemas.core.BlockSchema:
147
+ async with db.session_context() as session:
148
+ block_schema = await models.block_schemas.read_block_schema_by_checksum(
149
+ session=session, checksum=block_schema_checksum, version=version
150
+ )
151
+ if not block_schema:
152
+ raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Block schema not found")
153
+ return block_schema
@@ -0,0 +1,211 @@
1
+ from typing import List, Optional
2
+ from uuid import UUID
3
+
4
+ import sqlalchemy as sa
5
+ from fastapi import Body, Depends, HTTPException, Path, Query, status
6
+
7
+ from prefect.blocks.core import _should_update_block_type
8
+ from prefect.server import models, schemas
9
+ from prefect.server.api import dependencies
10
+ from prefect.server.database import PrefectDBInterface, provide_database_interface
11
+ from prefect.server.utilities.server import PrefectRouter
12
+
13
+ router: PrefectRouter = PrefectRouter(prefix="/block_types", tags=["Block types"])
14
+
15
+
16
+ @router.post("/", status_code=status.HTTP_201_CREATED)
17
+ async def create_block_type(
18
+ block_type: schemas.actions.BlockTypeCreate,
19
+ db: PrefectDBInterface = Depends(provide_database_interface),
20
+ ) -> schemas.core.BlockType:
21
+ """
22
+ Create a new block type
23
+ """
24
+ # API-created blocks cannot start with the word "Prefect"
25
+ # as it is reserved for system use
26
+ if block_type.name.lower().startswith("prefect"):
27
+ raise HTTPException(
28
+ status.HTTP_403_FORBIDDEN,
29
+ detail="Block type names beginning with 'Prefect' are reserved.",
30
+ )
31
+ try:
32
+ async with db.session_context(begin_transaction=True) as session:
33
+ created_block_type = await models.block_types.create_block_type(
34
+ session, block_type=block_type
35
+ )
36
+ except sa.exc.IntegrityError:
37
+ raise HTTPException(
38
+ status.HTTP_409_CONFLICT,
39
+ detail=f'Block type with name "{block_type.name}" already exists',
40
+ )
41
+ return created_block_type
42
+
43
+
44
+ @router.get("/{id}")
45
+ async def read_block_type_by_id(
46
+ block_type_id: UUID = Path(..., description="The block type ID", alias="id"),
47
+ db: PrefectDBInterface = Depends(provide_database_interface),
48
+ ) -> schemas.core.BlockType:
49
+ """
50
+ Get a block type by ID.
51
+ """
52
+ async with db.session_context() as session:
53
+ block_type = await models.block_types.read_block_type(
54
+ session=session, block_type_id=block_type_id
55
+ )
56
+ if not block_type:
57
+ raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Block type not found")
58
+ return block_type
59
+
60
+
61
+ @router.get("/slug/{slug}")
62
+ async def read_block_type_by_slug(
63
+ block_type_slug: str = Path(..., description="The block type name", alias="slug"),
64
+ db: PrefectDBInterface = Depends(provide_database_interface),
65
+ ) -> schemas.core.BlockType:
66
+ """
67
+ Get a block type by name.
68
+ """
69
+ async with db.session_context() as session:
70
+ block_type = await models.block_types.read_block_type_by_slug(
71
+ session=session, block_type_slug=block_type_slug
72
+ )
73
+ if not block_type:
74
+ raise HTTPException(status.HTTP_404_NOT_FOUND, detail="Block type not found")
75
+ return block_type
76
+
77
+
78
+ @router.post("/filter")
79
+ async def read_block_types(
80
+ block_types: Optional[schemas.filters.BlockTypeFilter] = None,
81
+ block_schemas: Optional[schemas.filters.BlockSchemaFilter] = None,
82
+ limit: int = dependencies.LimitBody(),
83
+ offset: int = Body(0, ge=0),
84
+ db: PrefectDBInterface = Depends(provide_database_interface),
85
+ ) -> List[schemas.core.BlockType]:
86
+ """
87
+ Gets all block types. Optionally limit return with limit and offset.
88
+ """
89
+ async with db.session_context() as session:
90
+ return await models.block_types.read_block_types(
91
+ session=session,
92
+ limit=limit,
93
+ offset=offset,
94
+ block_type_filter=block_types,
95
+ block_schema_filter=block_schemas,
96
+ )
97
+
98
+
99
+ @router.patch("/{id}", status_code=status.HTTP_204_NO_CONTENT)
100
+ async def update_block_type(
101
+ block_type: schemas.actions.BlockTypeUpdate,
102
+ block_type_id: UUID = Path(..., description="The block type ID", alias="id"),
103
+ db: PrefectDBInterface = Depends(provide_database_interface),
104
+ ) -> None:
105
+ """
106
+ Update a block type.
107
+ """
108
+ async with db.session_context(begin_transaction=True) as session:
109
+ db_block_type = await models.block_types.read_block_type(
110
+ session=session, block_type_id=block_type_id
111
+ )
112
+ if db_block_type is None:
113
+ raise HTTPException(
114
+ status.HTTP_404_NOT_FOUND, detail="Block type not found"
115
+ )
116
+
117
+ # Only update the block type if there is any meaningful changes.
118
+ # This avoids deadlocks when creating multiple blocks of the same type.
119
+ # This check happens client side, but we do it server side as well
120
+ # to accommodate older clients.
121
+ if _should_update_block_type(
122
+ block_type,
123
+ schemas.core.BlockType.model_validate(db_block_type, from_attributes=True),
124
+ ):
125
+ await models.block_types.update_block_type(
126
+ session=session, block_type=block_type, block_type_id=block_type_id
127
+ )
128
+
129
+
130
+ @router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT)
131
+ async def delete_block_type(
132
+ block_type_id: UUID = Path(..., description="The block type ID", alias="id"),
133
+ db: PrefectDBInterface = Depends(provide_database_interface),
134
+ ) -> None:
135
+ async with db.session_context(begin_transaction=True) as session:
136
+ db_block_type = await models.block_types.read_block_type(
137
+ session=session, block_type_id=block_type_id
138
+ )
139
+ if db_block_type is None:
140
+ raise HTTPException(
141
+ status_code=status.HTTP_404_NOT_FOUND, detail="Block type not found"
142
+ )
143
+ elif db_block_type.is_protected:
144
+ raise HTTPException(
145
+ status.HTTP_403_FORBIDDEN,
146
+ detail="protected block types cannot be deleted.",
147
+ )
148
+ await models.block_types.delete_block_type(
149
+ session=session, block_type_id=block_type_id
150
+ )
151
+
152
+
153
+ @router.get("/slug/{slug}/block_documents", tags=router.tags + ["Block documents"])
154
+ async def read_block_documents_for_block_type(
155
+ db: PrefectDBInterface = Depends(provide_database_interface),
156
+ block_type_slug: str = Path(..., description="The block type name", alias="slug"),
157
+ include_secrets: bool = Query(
158
+ False, description="Whether to include sensitive values in the block document."
159
+ ),
160
+ ) -> List[schemas.core.BlockDocument]:
161
+ async with db.session_context() as session:
162
+ block_type = await models.block_types.read_block_type_by_slug(
163
+ session=session, block_type_slug=block_type_slug
164
+ )
165
+ if not block_type:
166
+ raise HTTPException(
167
+ status.HTTP_404_NOT_FOUND, detail="Block type not found"
168
+ )
169
+ return await models.block_documents.read_block_documents(
170
+ session=session,
171
+ block_document_filter=schemas.filters.BlockDocumentFilter(
172
+ block_type_id=dict(any_=[block_type.id])
173
+ ),
174
+ include_secrets=include_secrets,
175
+ )
176
+
177
+
178
+ @router.get(
179
+ "/slug/{slug}/block_documents/name/{block_document_name}",
180
+ tags=router.tags + ["Block documents"],
181
+ )
182
+ async def read_block_document_by_name_for_block_type(
183
+ db: PrefectDBInterface = Depends(provide_database_interface),
184
+ block_type_slug: str = Path(..., description="The block type name", alias="slug"),
185
+ block_document_name: str = Path(..., description="The block type name"),
186
+ include_secrets: bool = Query(
187
+ False, description="Whether to include sensitive values in the block document."
188
+ ),
189
+ ) -> schemas.core.BlockDocument:
190
+ async with db.session_context() as session:
191
+ block_document = await models.block_documents.read_block_document_by_name(
192
+ session=session,
193
+ block_type_slug=block_type_slug,
194
+ name=block_document_name,
195
+ include_secrets=include_secrets,
196
+ )
197
+ if not block_document:
198
+ raise HTTPException(
199
+ status.HTTP_404_NOT_FOUND, detail="Block document not found"
200
+ )
201
+ return block_document
202
+
203
+
204
+ @router.post("/install_system_block_types")
205
+ async def install_system_block_types(
206
+ db: PrefectDBInterface = Depends(provide_database_interface),
207
+ ) -> None:
208
+ # Don't begin a transaction. _install_protected_system_blocks will manage
209
+ # the transactions.
210
+ async with db.session_context(begin_transaction=False) as session:
211
+ await models.block_registration._install_protected_system_blocks(session)