pydantic-encryption 0.0.2__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Julien Kmec
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,361 @@
1
+ Metadata-Version: 2.3
2
+ Name: pydantic_encryption
3
+ Version: 0.0.2
4
+ Summary: Encryption and Hashing Models for Pydantic
5
+ Author: Julien Kmec
6
+ Author-email: me@julien.dev
7
+ Requires-Python: >=3.11.0,<3.14
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Provides-Extra: dev
13
+ Provides-Extra: sqlalchemy
14
+ Requires-Dist: argon2-cffi (>=23.1.0,<24.0.0)
15
+ Requires-Dist: aws-encryption-sdk[mpl] (>=4.0.1,<5.0.0)
16
+ Requires-Dist: boto3 (>=1.38.8,<2.0.0)
17
+ Requires-Dist: coverage (>=7.8.0) ; extra == "dev"
18
+ Requires-Dist: evervault (>=4.4.1)
19
+ Requires-Dist: psycopg2-binary (>=2.9.10,<3.0.0) ; extra == "dev"
20
+ Requires-Dist: pydantic (>=2.10.6)
21
+ Requires-Dist: pydantic-settings (>=2.9.1)
22
+ Requires-Dist: pydantic-super-model (>=1.0.2,<2.0.0)
23
+ Requires-Dist: pytest (>=8.3.5) ; extra == "dev"
24
+ Requires-Dist: pytest-asyncio (>=0.26.0,<0.27.0) ; extra == "dev"
25
+ Requires-Dist: pytest-cov (>=6.1.1,<7.0.0) ; extra == "dev"
26
+ Requires-Dist: pytest-docker (>=3.2.1,<4.0.0) ; extra == "dev"
27
+ Requires-Dist: pytest-env (>=1.1.5,<2.0.0) ; extra == "dev"
28
+ Requires-Dist: pytest-sqlalchemy (>=0.3.0,<0.4.0) ; extra == "dev"
29
+ Requires-Dist: sqlalchemy (>=2.0.40,<3.0.0) ; extra == "sqlalchemy"
30
+ Requires-Dist: sqlalchemy-utils (>=0.41.2,<0.42.0) ; extra == "dev"
31
+ Requires-Dist: sqlmodel (>=0.0.24,<0.0.25) ; extra == "sqlalchemy"
32
+ Description-Content-Type: text/markdown
33
+
34
+ # Encryption and Hashing Models for Pydantic
35
+
36
+ This package provides Pydantic field annotations that encrypt, decrypt, and hash field values.
37
+
38
+ ## Installation
39
+
40
+ Install with [pip](https://pip.pypa.io/en/stable/):
41
+ ```bash
42
+ pip install "pydantic_encryption[sqlalchemy]"
43
+ ```
44
+
45
+ Install with [Poetry](https://python-poetry.org/docs/):
46
+ ```bash
47
+ poetry add pydantic_encryption --E sqlalchemy
48
+ ```
49
+
50
+ ### Optional extras
51
+
52
+ - `sqlalchemy`: Built-in SQLAlchemy integration
53
+ - `dev`: Development and test dependencies
54
+
55
+ ## Features
56
+
57
+ - Encrypt and decrypt specific fields
58
+ - Hash specific fields
59
+ - Built-in SQLAlchemy integration
60
+ - Support for AWS KMS (Key Management Service) single-region
61
+ - Support for Fernet symmetric encryption and Evervault
62
+ - Support for generics
63
+
64
+ ## Example
65
+
66
+ ```python
67
+ from typing import Annotated
68
+ from pydantic_encryption import BaseModel, Encrypt, Hash
69
+
70
+ class User(BaseModel):
71
+ name: str
72
+ address: Annotated[bytes, Encrypt] # This field will be encrypted
73
+ password: Annotated[bytes, Hash] # This field will be hashed
74
+
75
+ user = User(name="John Doe", address="123456", password="secret123")
76
+
77
+ print(user.name) # plaintext (untouched)
78
+ print(user.address) # encrypted
79
+ print(user.password) # hashed
80
+ ```
81
+
82
+ ## SQLAlchemy Integration
83
+
84
+ If you install this package with the `sqlalchemy` extra, you can use the built-in SQLAlchemy integration for the columns.
85
+
86
+ SQLAlchemy will automatically handle the encryption/decryption of fields with the `SQLAlchemyEncrypted` type and the hashing of fields with the `SQLAlchemyHashed` type.
87
+
88
+ When you create a new instance of the model, the fields will be encrypted and when you query the database, the fields will be decrypted.
89
+
90
+ ### Example:
91
+
92
+ ```python
93
+ import uuid
94
+ from pydantic_encryption import SQLAlchemyEncrypted, SQLAlchemyHashed
95
+ from sqlmodel import SQLModel, Field
96
+ from sqlalchemy import create_engine
97
+ from sqlalchemy.orm import sessionmaker
98
+
99
+ # Define our schema
100
+ class User(Base, table=True):
101
+ __tablename__ = "users"
102
+
103
+ username: str = Field(default=None)
104
+ email: bytes = Field(
105
+ default=None,
106
+ sa_type=SQLAlchemyEncrypted(),
107
+ )
108
+ password: bytes = Field(
109
+ sa_type=SQLAlchemyHashed(),
110
+ nullable=False,
111
+ )
112
+
113
+ # Create the database
114
+ engine = create_engine("sqlite:///:memory:")
115
+ SQLModel.metadata.create_all(engine)
116
+ Session = sessionmaker(bind=engine)
117
+ session = Session()
118
+
119
+ # Create a user
120
+ user = User(username="john_doe", email="john@example.com", password="secret123") # The email and password will be encrypted/hashed automatically
121
+
122
+ session.add(user)
123
+ session.commit()
124
+
125
+ # Query the user
126
+ user = session.query(User).filter_by(username="john_doe").first()
127
+
128
+ print(user.email) # decrypted
129
+ print(user.password) # hashed
130
+ ```
131
+
132
+ ## Choose an Encryption Method
133
+
134
+ You can choose which encryption algorithm to use by setting the `ENCRYPTION_METHOD` environment variable.
135
+
136
+ Valid values are:
137
+ - `fernet`: Fernet symmetric encryption
138
+ - `aws`: AWS KMS
139
+ - `evervault`: [Evervault](https://evervault.com/)
140
+
141
+ See [config.py](https://github.com/julien777z/pydantic-encryption/blob/main/pydantic_encryption/config.py) for the possible environment variables.
142
+
143
+ ### Example:
144
+
145
+ `.env`
146
+ ```env
147
+ ENCRYPTION_METHOD=aws
148
+ AWS_KMS_KEY_ARN=123
149
+ AWS_KMS_REGION=us-east-1
150
+ AWS_ACCESS_KEY_ID=123
151
+ AWS_SECRET_ACCESS_KEY=123
152
+ ```
153
+
154
+ ```python
155
+ from typing import Annotated
156
+ from pydantic_encryption import BaseModel, Encrypt
157
+
158
+ class User(BaseModel):
159
+ name: str
160
+ address: Annotated[bytes, Encrypt] # This field will be encrypted by AWS KMS
161
+ ```
162
+
163
+ ### Default Encryption (Fernet Symmetric Encryption)
164
+
165
+ By default, Fernet will be used for encryption and decryption.
166
+
167
+ First you need to generate an encryption key. You can use the following command:
168
+
169
+ ```bash
170
+ openssl rand -base64 32
171
+ ```
172
+
173
+ Then set the following environment variable or add it to your `.env` file:
174
+
175
+ ```bash
176
+ ENCRYPTION_KEY=your_encryption_key
177
+ ```
178
+
179
+ ### Custom Encryption or Hashing
180
+
181
+ You can define your own encryption or hashing methods by subclassing `SecureModel`. `SecureModel` provides you with the utilities to handle encryption, decryption, and hashing.
182
+
183
+ `self.pending_encryption_fields`, `self.pending_decryption_fields`, and `self.pending_hash_fields` are dictionaries of field names to field values that need to be encrypted, decrypted, or hashed, i.e., fields annotated with `Encrypt`, `Decrypt`, or `Hash`.
184
+
185
+ You can override the `encrypt_data`, `decrypt_data`, and `hash_data` methods to implement your own encryption, decryption, and hashing logic. You then need to override `model_post_init` to call these methods or use the default implementation accessible via `self.default_post_init()`.
186
+
187
+ First, define a custom secure model:
188
+
189
+ ```python
190
+ from typing import Any, override
191
+ from pydantic import BaseModel as PydanticBaseModel
192
+ from pydantic_encryption import SecureModel
193
+
194
+ class MySecureModel(PydanticBaseModel, SecureModel):
195
+ @override
196
+ def encrypt_data(self) -> None:
197
+ # Your encryption logic here
198
+ pass
199
+
200
+ @override
201
+ def decrypt_data(self) -> None:
202
+ # Your decryption logic here
203
+ pass
204
+
205
+ @override
206
+ def hash_data(self) -> None:
207
+ # Your hashing logic here
208
+ pass
209
+
210
+ @override
211
+ def model_post_init(self, context: Any, /) -> None:
212
+ # Either define your own logic, for example:
213
+
214
+ # if not self._disable:
215
+ # if self.pending_decryption_fields:
216
+ # self.decrypt_data()
217
+
218
+ # if self.pending_encryption_fields:
219
+ # self.encrypt_data()
220
+
221
+ # if self.pending_hash_fields:
222
+ # self.hash_data()
223
+
224
+ # Or use the default logic:
225
+ self.default_post_init()
226
+
227
+ super().model_post_init(context)
228
+ ```
229
+
230
+ Then use it:
231
+
232
+ ```python
233
+ from typing import Annotated
234
+ from pydantic import BaseModel # Here, we don't use the BaseModel provided by the library, but the native one from Pydantic
235
+ from pydantic_encryption import Encrypt
236
+
237
+ class MyModel(BaseModel, MySecureModel):
238
+ username: str
239
+ address: Annotated[bytes, Encrypt]
240
+
241
+ model = MyModel(username="john_doe", address="123456")
242
+ print(model.address) # encrypted
243
+ ```
244
+
245
+ ## Encryption
246
+
247
+ You can encrypt any field by using the `Encrypt` annotation with `Annotated` and inheriting from `BaseModel`.
248
+
249
+ ```python
250
+ from typing import Annotated
251
+ from pydantic_encryption import Encrypt, BaseModel
252
+
253
+ class User(BaseModel):
254
+ name: str
255
+ address: Annotated[bytes, Encrypt] # This field will be encrypted
256
+
257
+ user = User(name="John Doe", address="123456")
258
+ print(user.address) # encrypted
259
+ print(user.name) # plaintext (untouched)
260
+ ```
261
+
262
+ The fields marked with `Encrypt` are automatically encrypted during model initialization.
263
+
264
+ ## Decryption
265
+
266
+ Similar to encryption, you can decrypt any field by using the `Decrypt` annotation with `Annotated` and inheriting from `BaseModel`.
267
+
268
+ ```python
269
+ from typing import Annotated
270
+ from pydantic_encryption import Decrypt, BaseModel
271
+
272
+ class UserResponse(BaseModel):
273
+ name: str
274
+ address: Annotated[bytes, Decrypt] # This field will be decrypted
275
+
276
+ user = UserResponse(**user_data) # encrypted value
277
+ print(user.address) # decrypted
278
+ print(user.name) # plaintext (untouched)
279
+ ```
280
+
281
+ Fields marked with `Decrypt` are automatically decrypted during model initialization.
282
+
283
+ Note: if you use `SQLAlchemyEncrypted`, then the value will be decrypted automatically when you query the database.
284
+
285
+
286
+ ## Hashing
287
+
288
+ You can hash sensitive data like passwords by using the `Hash` annotation.
289
+
290
+ ```python
291
+ from typing import Annotated
292
+ from pydantic_encryption import Hash, BaseModel
293
+
294
+ class User(BaseModel):
295
+ username: str
296
+ password: Annotated[bytes, Hash] # This field will be hashed
297
+
298
+ user = User(username="john_doe", password="secret123")
299
+ print(user.password) # hashed value
300
+ ```
301
+
302
+ Fields marked with `Hash` are automatically hashed using bcrypt during model initialization.
303
+
304
+ ## Disable Auto Processing
305
+
306
+ You can disable automatic encryption/decryption/hashing by setting `disable` to `True` in the class definition.
307
+
308
+ ```python
309
+ from typing import Annotated
310
+ from pydantic_encryption import Encrypt, BaseModel
311
+
312
+ class UserResponse(BaseModel, disable=True):
313
+ name: str
314
+ address: Annotated[bytes, Encrypt]
315
+
316
+ # To encrypt/decrypt/hash, call the respective methods manually:
317
+ user = UserResponse(name="John Doe", address="123 Main St")
318
+
319
+ # Manual encryption
320
+ user.encrypt_data()
321
+ print(user.address) # encrypted
322
+
323
+ # Or user.decrypt_data() to decrypt and user.hash_data() to hash
324
+ ```
325
+
326
+ ## Generics
327
+
328
+ Each BaseModel has an additional helpful method that will tell you its generic type.
329
+
330
+ ```py
331
+ from pydantic_encryption import BaseModel
332
+
333
+ class MyModel[T](BaseModel):
334
+ value: T
335
+
336
+ model = MyModel[str](value="Hello")
337
+ print(model.get_type()) # <class 'str'>
338
+ ```
339
+
340
+ ## Run Tests
341
+
342
+ Install [Poetry](https://python-poetry.org/docs/) and run:
343
+
344
+ ```bash
345
+ poetry install --with test
346
+ poetry run coverage run -m pytest -v -s
347
+ ```
348
+
349
+ ## Roadmap
350
+
351
+ This is an early development version. I am considering the following features:
352
+
353
+ - [ ] Add optional support for other encryption providers beyond Evervault
354
+ - [x] Add support for AWS KMS and other key management services
355
+ - [ ] Native encryption via PostgreSQL and other databases
356
+ - [ ] Specifying encryption key per table or row instead of globally
357
+
358
+ ## Feature Requests
359
+
360
+ If you have any feature requests, please open an issue.
361
+
@@ -0,0 +1,327 @@
1
+ # Encryption and Hashing Models for Pydantic
2
+
3
+ This package provides Pydantic field annotations that encrypt, decrypt, and hash field values.
4
+
5
+ ## Installation
6
+
7
+ Install with [pip](https://pip.pypa.io/en/stable/):
8
+ ```bash
9
+ pip install "pydantic_encryption[sqlalchemy]"
10
+ ```
11
+
12
+ Install with [Poetry](https://python-poetry.org/docs/):
13
+ ```bash
14
+ poetry add pydantic_encryption --E sqlalchemy
15
+ ```
16
+
17
+ ### Optional extras
18
+
19
+ - `sqlalchemy`: Built-in SQLAlchemy integration
20
+ - `dev`: Development and test dependencies
21
+
22
+ ## Features
23
+
24
+ - Encrypt and decrypt specific fields
25
+ - Hash specific fields
26
+ - Built-in SQLAlchemy integration
27
+ - Support for AWS KMS (Key Management Service) single-region
28
+ - Support for Fernet symmetric encryption and Evervault
29
+ - Support for generics
30
+
31
+ ## Example
32
+
33
+ ```python
34
+ from typing import Annotated
35
+ from pydantic_encryption import BaseModel, Encrypt, Hash
36
+
37
+ class User(BaseModel):
38
+ name: str
39
+ address: Annotated[bytes, Encrypt] # This field will be encrypted
40
+ password: Annotated[bytes, Hash] # This field will be hashed
41
+
42
+ user = User(name="John Doe", address="123456", password="secret123")
43
+
44
+ print(user.name) # plaintext (untouched)
45
+ print(user.address) # encrypted
46
+ print(user.password) # hashed
47
+ ```
48
+
49
+ ## SQLAlchemy Integration
50
+
51
+ If you install this package with the `sqlalchemy` extra, you can use the built-in SQLAlchemy integration for the columns.
52
+
53
+ SQLAlchemy will automatically handle the encryption/decryption of fields with the `SQLAlchemyEncrypted` type and the hashing of fields with the `SQLAlchemyHashed` type.
54
+
55
+ When you create a new instance of the model, the fields will be encrypted and when you query the database, the fields will be decrypted.
56
+
57
+ ### Example:
58
+
59
+ ```python
60
+ import uuid
61
+ from pydantic_encryption import SQLAlchemyEncrypted, SQLAlchemyHashed
62
+ from sqlmodel import SQLModel, Field
63
+ from sqlalchemy import create_engine
64
+ from sqlalchemy.orm import sessionmaker
65
+
66
+ # Define our schema
67
+ class User(Base, table=True):
68
+ __tablename__ = "users"
69
+
70
+ username: str = Field(default=None)
71
+ email: bytes = Field(
72
+ default=None,
73
+ sa_type=SQLAlchemyEncrypted(),
74
+ )
75
+ password: bytes = Field(
76
+ sa_type=SQLAlchemyHashed(),
77
+ nullable=False,
78
+ )
79
+
80
+ # Create the database
81
+ engine = create_engine("sqlite:///:memory:")
82
+ SQLModel.metadata.create_all(engine)
83
+ Session = sessionmaker(bind=engine)
84
+ session = Session()
85
+
86
+ # Create a user
87
+ user = User(username="john_doe", email="john@example.com", password="secret123") # The email and password will be encrypted/hashed automatically
88
+
89
+ session.add(user)
90
+ session.commit()
91
+
92
+ # Query the user
93
+ user = session.query(User).filter_by(username="john_doe").first()
94
+
95
+ print(user.email) # decrypted
96
+ print(user.password) # hashed
97
+ ```
98
+
99
+ ## Choose an Encryption Method
100
+
101
+ You can choose which encryption algorithm to use by setting the `ENCRYPTION_METHOD` environment variable.
102
+
103
+ Valid values are:
104
+ - `fernet`: Fernet symmetric encryption
105
+ - `aws`: AWS KMS
106
+ - `evervault`: [Evervault](https://evervault.com/)
107
+
108
+ See [config.py](https://github.com/julien777z/pydantic-encryption/blob/main/pydantic_encryption/config.py) for the possible environment variables.
109
+
110
+ ### Example:
111
+
112
+ `.env`
113
+ ```env
114
+ ENCRYPTION_METHOD=aws
115
+ AWS_KMS_KEY_ARN=123
116
+ AWS_KMS_REGION=us-east-1
117
+ AWS_ACCESS_KEY_ID=123
118
+ AWS_SECRET_ACCESS_KEY=123
119
+ ```
120
+
121
+ ```python
122
+ from typing import Annotated
123
+ from pydantic_encryption import BaseModel, Encrypt
124
+
125
+ class User(BaseModel):
126
+ name: str
127
+ address: Annotated[bytes, Encrypt] # This field will be encrypted by AWS KMS
128
+ ```
129
+
130
+ ### Default Encryption (Fernet Symmetric Encryption)
131
+
132
+ By default, Fernet will be used for encryption and decryption.
133
+
134
+ First you need to generate an encryption key. You can use the following command:
135
+
136
+ ```bash
137
+ openssl rand -base64 32
138
+ ```
139
+
140
+ Then set the following environment variable or add it to your `.env` file:
141
+
142
+ ```bash
143
+ ENCRYPTION_KEY=your_encryption_key
144
+ ```
145
+
146
+ ### Custom Encryption or Hashing
147
+
148
+ You can define your own encryption or hashing methods by subclassing `SecureModel`. `SecureModel` provides you with the utilities to handle encryption, decryption, and hashing.
149
+
150
+ `self.pending_encryption_fields`, `self.pending_decryption_fields`, and `self.pending_hash_fields` are dictionaries of field names to field values that need to be encrypted, decrypted, or hashed, i.e., fields annotated with `Encrypt`, `Decrypt`, or `Hash`.
151
+
152
+ You can override the `encrypt_data`, `decrypt_data`, and `hash_data` methods to implement your own encryption, decryption, and hashing logic. You then need to override `model_post_init` to call these methods or use the default implementation accessible via `self.default_post_init()`.
153
+
154
+ First, define a custom secure model:
155
+
156
+ ```python
157
+ from typing import Any, override
158
+ from pydantic import BaseModel as PydanticBaseModel
159
+ from pydantic_encryption import SecureModel
160
+
161
+ class MySecureModel(PydanticBaseModel, SecureModel):
162
+ @override
163
+ def encrypt_data(self) -> None:
164
+ # Your encryption logic here
165
+ pass
166
+
167
+ @override
168
+ def decrypt_data(self) -> None:
169
+ # Your decryption logic here
170
+ pass
171
+
172
+ @override
173
+ def hash_data(self) -> None:
174
+ # Your hashing logic here
175
+ pass
176
+
177
+ @override
178
+ def model_post_init(self, context: Any, /) -> None:
179
+ # Either define your own logic, for example:
180
+
181
+ # if not self._disable:
182
+ # if self.pending_decryption_fields:
183
+ # self.decrypt_data()
184
+
185
+ # if self.pending_encryption_fields:
186
+ # self.encrypt_data()
187
+
188
+ # if self.pending_hash_fields:
189
+ # self.hash_data()
190
+
191
+ # Or use the default logic:
192
+ self.default_post_init()
193
+
194
+ super().model_post_init(context)
195
+ ```
196
+
197
+ Then use it:
198
+
199
+ ```python
200
+ from typing import Annotated
201
+ from pydantic import BaseModel # Here, we don't use the BaseModel provided by the library, but the native one from Pydantic
202
+ from pydantic_encryption import Encrypt
203
+
204
+ class MyModel(BaseModel, MySecureModel):
205
+ username: str
206
+ address: Annotated[bytes, Encrypt]
207
+
208
+ model = MyModel(username="john_doe", address="123456")
209
+ print(model.address) # encrypted
210
+ ```
211
+
212
+ ## Encryption
213
+
214
+ You can encrypt any field by using the `Encrypt` annotation with `Annotated` and inheriting from `BaseModel`.
215
+
216
+ ```python
217
+ from typing import Annotated
218
+ from pydantic_encryption import Encrypt, BaseModel
219
+
220
+ class User(BaseModel):
221
+ name: str
222
+ address: Annotated[bytes, Encrypt] # This field will be encrypted
223
+
224
+ user = User(name="John Doe", address="123456")
225
+ print(user.address) # encrypted
226
+ print(user.name) # plaintext (untouched)
227
+ ```
228
+
229
+ The fields marked with `Encrypt` are automatically encrypted during model initialization.
230
+
231
+ ## Decryption
232
+
233
+ Similar to encryption, you can decrypt any field by using the `Decrypt` annotation with `Annotated` and inheriting from `BaseModel`.
234
+
235
+ ```python
236
+ from typing import Annotated
237
+ from pydantic_encryption import Decrypt, BaseModel
238
+
239
+ class UserResponse(BaseModel):
240
+ name: str
241
+ address: Annotated[bytes, Decrypt] # This field will be decrypted
242
+
243
+ user = UserResponse(**user_data) # encrypted value
244
+ print(user.address) # decrypted
245
+ print(user.name) # plaintext (untouched)
246
+ ```
247
+
248
+ Fields marked with `Decrypt` are automatically decrypted during model initialization.
249
+
250
+ Note: if you use `SQLAlchemyEncrypted`, then the value will be decrypted automatically when you query the database.
251
+
252
+
253
+ ## Hashing
254
+
255
+ You can hash sensitive data like passwords by using the `Hash` annotation.
256
+
257
+ ```python
258
+ from typing import Annotated
259
+ from pydantic_encryption import Hash, BaseModel
260
+
261
+ class User(BaseModel):
262
+ username: str
263
+ password: Annotated[bytes, Hash] # This field will be hashed
264
+
265
+ user = User(username="john_doe", password="secret123")
266
+ print(user.password) # hashed value
267
+ ```
268
+
269
+ Fields marked with `Hash` are automatically hashed using bcrypt during model initialization.
270
+
271
+ ## Disable Auto Processing
272
+
273
+ You can disable automatic encryption/decryption/hashing by setting `disable` to `True` in the class definition.
274
+
275
+ ```python
276
+ from typing import Annotated
277
+ from pydantic_encryption import Encrypt, BaseModel
278
+
279
+ class UserResponse(BaseModel, disable=True):
280
+ name: str
281
+ address: Annotated[bytes, Encrypt]
282
+
283
+ # To encrypt/decrypt/hash, call the respective methods manually:
284
+ user = UserResponse(name="John Doe", address="123 Main St")
285
+
286
+ # Manual encryption
287
+ user.encrypt_data()
288
+ print(user.address) # encrypted
289
+
290
+ # Or user.decrypt_data() to decrypt and user.hash_data() to hash
291
+ ```
292
+
293
+ ## Generics
294
+
295
+ Each BaseModel has an additional helpful method that will tell you its generic type.
296
+
297
+ ```py
298
+ from pydantic_encryption import BaseModel
299
+
300
+ class MyModel[T](BaseModel):
301
+ value: T
302
+
303
+ model = MyModel[str](value="Hello")
304
+ print(model.get_type()) # <class 'str'>
305
+ ```
306
+
307
+ ## Run Tests
308
+
309
+ Install [Poetry](https://python-poetry.org/docs/) and run:
310
+
311
+ ```bash
312
+ poetry install --with test
313
+ poetry run coverage run -m pytest -v -s
314
+ ```
315
+
316
+ ## Roadmap
317
+
318
+ This is an early development version. I am considering the following features:
319
+
320
+ - [ ] Add optional support for other encryption providers beyond Evervault
321
+ - [x] Add support for AWS KMS and other key management services
322
+ - [ ] Native encryption via PostgreSQL and other databases
323
+ - [ ] Specifying encryption key per table or row instead of globally
324
+
325
+ ## Feature Requests
326
+
327
+ If you have any feature requests, please open an issue.
@@ -0,0 +1,3 @@
1
+ from .models import *
2
+ from .annotations import *
3
+ from .lib import *