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