fastlib-py 0.2.6__tar.gz → 0.2.7__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.
Files changed (71) hide show
  1. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/PKG-INFO +1 -1
  2. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/config/loader.py +5 -1
  3. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/config/manager.py +0 -3
  4. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/config/utils.py +5 -1
  5. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/security/utils.py +262 -116
  6. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib_py.egg-info/PKG-INFO +1 -1
  7. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/pyproject.toml +127 -126
  8. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/LICENSE +0 -0
  9. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/README.md +0 -0
  10. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/__init__.py +0 -0
  11. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/cache/__init__.py +0 -0
  12. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/cache/base.py +0 -0
  13. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/cache/disk_cache.py +0 -0
  14. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/cache/manager.py +0 -0
  15. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/cache/redis_cache.py +0 -0
  16. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/config/__init__.py +0 -0
  17. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/config/base.py +0 -0
  18. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/config/database_config.py +0 -0
  19. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/config/registry.py +0 -0
  20. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/config/security_config.py +0 -0
  21. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/config/server_config.py +0 -0
  22. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/constant/__init__.py +0 -0
  23. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/constant/constant.py +0 -0
  24. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/contextvars.py +0 -0
  25. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/enums.py +0 -0
  26. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/exception/__init__.py +0 -0
  27. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/exception/base.py +0 -0
  28. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/exception/exception_handler.py +0 -0
  29. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/exception/exception_manager.py +0 -0
  30. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/i18n/__init__.py +0 -0
  31. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/i18n/manager.py +0 -0
  32. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/i18n/messages.py +0 -0
  33. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/i18n/types.py +0 -0
  34. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/i18n/utils.py +0 -0
  35. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/logging/__init__.py +0 -0
  36. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/logging/config.py +0 -0
  37. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/logging/handlers.py +0 -0
  38. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/mapper/__init__.py +0 -0
  39. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/mapper/base_mapper.py +0 -0
  40. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/mapper/impl/__init__.py +0 -0
  41. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/mapper/impl/base_mapper_impl.py +0 -0
  42. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/middleware/__init__.py +0 -0
  43. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/middleware/db_session.py +0 -0
  44. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/middleware/i18n.py +0 -0
  45. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/middleware/jwt.py +0 -0
  46. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/migrate.py +0 -0
  47. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/openapi.py +0 -0
  48. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/request.py +0 -0
  49. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/response.py +0 -0
  50. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/router.py +0 -0
  51. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/schema.py +0 -0
  52. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/security/__init__.py +0 -0
  53. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/security/security.py +0 -0
  54. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/service/__init__.py +0 -0
  55. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/service/base_service.py +0 -0
  56. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/service/impl/__init__.py +0 -0
  57. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/service/impl/base_service_impl.py +0 -0
  58. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/session/__init__.py +0 -0
  59. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/session/db_engine.py +0 -0
  60. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/utils/__init__.py +0 -0
  61. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/utils/excel_util.py +0 -0
  62. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/utils/file_util.py +0 -0
  63. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/utils/http_util.py +0 -0
  64. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/utils/ip_util.py +0 -0
  65. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/utils/snowflake_util.py +0 -0
  66. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib/utils/str_util.py +0 -0
  67. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib_py.egg-info/SOURCES.txt +0 -0
  68. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib_py.egg-info/dependency_links.txt +0 -0
  69. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib_py.egg-info/requires.txt +0 -0
  70. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/fastlib_py.egg-info/top_level.txt +0 -0
  71. {fastlib_py-0.2.6 → fastlib_py-0.2.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastlib-py
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: Fastlib, high performance, easy to use, fast to code, ready for production
5
5
  Author-email: tyvekZhang <tyvekzhang@gmail.com>
6
6
  Maintainer-email: tyvekZhang <tyvekzhang@gmail.com>
@@ -5,6 +5,7 @@ import os
5
5
  from typing import Any, Optional
6
6
 
7
7
  import yaml
8
+ from loguru import logger
8
9
 
9
10
  from fastlib import constant
10
11
  from fastlib.config import utils as config_util
@@ -47,7 +48,10 @@ class ConfigLoader:
47
48
  yaml.YAMLError: If there is an error parsing the YAML
48
49
  """
49
50
  if not os.path.exists(file_path):
50
- raise FileNotFoundError(f"Config file not found: {file_path}")
51
+ logger.warning(
52
+ f"Config file not found: {file_path}, will use the default config"
53
+ )
54
+ return {}
51
55
 
52
56
  with open(file_path, encoding="utf-8") as file:
53
57
  try:
@@ -123,9 +123,6 @@ class ConfigManager:
123
123
  for config_name in ConfigRegistry.list_registered():
124
124
  config_data = ConfigManager._global_config_dict.get(config_name, {})
125
125
 
126
- if not config_data:
127
- continue
128
-
129
126
  try:
130
127
  instance = ConfigRegistry.create_instance(
131
128
  config_name, {config_name: config_data}
@@ -172,7 +172,11 @@ def get_alembic_db_info(
172
172
 
173
173
  def get_db_url() -> str:
174
174
  """Get the database connection URL from alembic.ini configuration."""
175
- return get_alembic_db_info(file_util.get_file_path("alembic.ini")).full_url
175
+ try:
176
+ return get_alembic_db_info(file_util.get_file_path("alembic.ini")).full_url
177
+ except FileNotFoundError:
178
+ logger.warning("Can not found alembic.ini in the project, use default db url")
179
+ return "sqlite+aiosqlite:///./fastlib.db"
176
180
 
177
181
 
178
182
  def get_sqlite_db_path() -> str:
@@ -10,7 +10,7 @@ from typing import Optional, Union
10
10
  try:
11
11
  from cryptography.fernet import Fernet
12
12
  from cryptography.hazmat.primitives import hashes, serialization
13
- from cryptography.hazmat.primitives.asymmetric import padding, rsa
13
+ from cryptography.hazmat.primitives.asymmetric import ed25519, padding, rsa
14
14
  from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
15
15
 
16
16
  HAS_CRYPTOGRAPHY = True
@@ -137,110 +137,6 @@ class SymmetricEncryption:
137
137
  return decrypted.decode("utf-8")
138
138
 
139
139
 
140
- class SimpleSymmetricEncryption:
141
- """Simple symmetric encryption using XOR and HMAC (when cryptography is not available)."""
142
-
143
- def __init__(self, key: Optional[bytes] = None) -> None:
144
- """
145
- Initialize simple symmetric encryption with a key.
146
-
147
- Args:
148
- key: Encryption key. If None, generates a new key.
149
- """
150
- if key is None:
151
- self._key = os.urandom(32)
152
- else:
153
- self._key = key[:32] if len(key) > 32 else key.ljust(32, b"\x00")
154
-
155
- def get_key(self) -> bytes:
156
- """Get the encryption key."""
157
- return self._key
158
-
159
- def _xor_encrypt_decrypt(self, data: bytes) -> bytes:
160
- """XOR encryption/decryption."""
161
- key_len = len(self._key)
162
- return bytes(data[i] ^ self._key[i % key_len] for i in range(len(data)))
163
-
164
- def encrypt(self, plaintext: Union[str, bytes]) -> bytes:
165
- """
166
- Encrypt plaintext data using XOR + HMAC.
167
-
168
- Args:
169
- plaintext: Data to encrypt
170
-
171
- Returns:
172
- Encrypted data with HMAC
173
- """
174
- if isinstance(plaintext, str):
175
- plaintext = plaintext.encode("utf-8")
176
-
177
- # Add random IV
178
- iv = os.urandom(16)
179
-
180
- # XOR encrypt
181
- encrypted = self._xor_encrypt_decrypt(plaintext)
182
-
183
- # Create HMAC for integrity
184
- mac = hmac.new(self._key, iv + encrypted, hashlib.sha256).digest()
185
-
186
- return iv + mac + encrypted
187
-
188
- def decrypt(self, ciphertext: bytes) -> bytes:
189
- """
190
- Decrypt ciphertext data.
191
-
192
- Args:
193
- ciphertext: Encrypted data to decrypt
194
-
195
- Returns:
196
- Decrypted data
197
-
198
- Raises:
199
- ValueError: If decryption fails or HMAC verification fails
200
- """
201
- if len(ciphertext) < 48: # 16 (IV) + 32 (HMAC) = 48 minimum
202
- raise ValueError("Invalid ciphertext length")
203
-
204
- iv = ciphertext[:16]
205
- mac = ciphertext[16:48]
206
- encrypted = ciphertext[48:]
207
-
208
- # Verify HMAC
209
- expected_mac = hmac.new(self._key, iv + encrypted, hashlib.sha256).digest()
210
- if not hmac.compare_digest(mac, expected_mac):
211
- raise ValueError("HMAC verification failed")
212
-
213
- # XOR decrypt
214
- return self._xor_encrypt_decrypt(encrypted)
215
-
216
- def encrypt_string(self, plaintext: str) -> str:
217
- """
218
- Encrypt string and return base64 encoded result.
219
-
220
- Args:
221
- plaintext: String to encrypt
222
-
223
- Returns:
224
- Base64 encoded encrypted string
225
- """
226
- encrypted = self.encrypt(plaintext)
227
- return base64.b64encode(encrypted).decode("utf-8")
228
-
229
- def decrypt_string(self, ciphertext: str) -> str:
230
- """
231
- Decrypt base64 encoded string.
232
-
233
- Args:
234
- ciphertext: Base64 encoded encrypted string
235
-
236
- Returns:
237
- Decrypted string
238
- """
239
- encrypted_bytes = base64.b64decode(ciphertext.encode("utf-8"))
240
- decrypted = self.decrypt(encrypted_bytes)
241
- return decrypted.decode("utf-8")
242
-
243
-
244
140
  class HMACSigner:
245
141
  """HMAC-based message signing and verification."""
246
142
 
@@ -463,6 +359,248 @@ class RSASigner:
463
359
  return False
464
360
 
465
361
 
362
+ class Ed25519Signer:
363
+ """Ed25519 elliptic curve digital signing and verification."""
364
+
365
+ def __init__(
366
+ self, private_key: Optional[bytes] = None, public_key: Optional[bytes] = None
367
+ ) -> None:
368
+ """
369
+ Initialize Ed25519 signer.
370
+
371
+ Args:
372
+ private_key: PEM encoded private key for signing
373
+ public_key: PEM encoded public key for verification
374
+
375
+ Raises:
376
+ ImportError: If cryptography package is not available
377
+ """
378
+ if not HAS_CRYPTOGRAPHY:
379
+ raise ImportError(
380
+ "cryptography package is required for Ed25519Signer. Install it with: uv add cryptography"
381
+ )
382
+
383
+ self._private_key = None
384
+ self._public_key = None
385
+
386
+ if private_key:
387
+ self._private_key = serialization.load_pem_private_key(
388
+ private_key, password=None
389
+ )
390
+ # Validate that it's an Ed25519 key
391
+ if not isinstance(self._private_key, ed25519.Ed25519PrivateKey):
392
+ raise ValueError("Private key is not an Ed25519 key")
393
+
394
+ if public_key:
395
+ self._public_key = serialization.load_pem_public_key(public_key)
396
+ # Validate that it's an Ed25519 key
397
+ if not isinstance(self._public_key, ed25519.Ed25519PublicKey):
398
+ raise ValueError("Public key is not an Ed25519 key")
399
+
400
+ @classmethod
401
+ def generate_keypair(cls) -> tuple[bytes, bytes]:
402
+ """
403
+ Generate Ed25519 key pair.
404
+
405
+ Returns:
406
+ Tuple of (private_key_pem, public_key_pem)
407
+
408
+ Raises:
409
+ ImportError: If cryptography package is not available
410
+ """
411
+ if not HAS_CRYPTOGRAPHY:
412
+ raise ImportError(
413
+ "cryptography package is required for Ed25519Signer. Install it with: uv add cryptography"
414
+ )
415
+
416
+ # Generate private key
417
+ private_key = ed25519.Ed25519PrivateKey.generate()
418
+
419
+ # Serialize private key
420
+ private_pem = private_key.private_bytes(
421
+ encoding=serialization.Encoding.PEM,
422
+ format=serialization.PrivateFormat.PKCS8,
423
+ encryption_algorithm=serialization.NoEncryption(),
424
+ )
425
+
426
+ # Serialize public key
427
+ public_pem = private_key.public_key().public_bytes(
428
+ encoding=serialization.Encoding.PEM,
429
+ format=serialization.PublicFormat.SubjectPublicKeyInfo,
430
+ )
431
+
432
+ return private_pem, public_pem
433
+
434
+ @classmethod
435
+ def from_private_key_bytes(cls, private_key_bytes: bytes) -> "Ed25519Signer":
436
+ """
437
+ Create Ed25519Signer from raw private key bytes.
438
+
439
+ Args:
440
+ private_key_bytes: Raw 32-byte private key
441
+
442
+ Returns:
443
+ Ed25519Signer instance
444
+ """
445
+ if not HAS_CRYPTOGRAPHY:
446
+ raise ImportError(
447
+ "cryptography package is required for Ed25519Signer. Install it with: uv add cryptography"
448
+ )
449
+
450
+ private_key = ed25519.Ed25519PrivateKey.from_private_bytes(private_key_bytes)
451
+ private_pem = private_key.private_bytes(
452
+ encoding=serialization.Encoding.PEM,
453
+ format=serialization.PrivateFormat.PKCS8,
454
+ encryption_algorithm=serialization.NoEncryption(),
455
+ )
456
+ return cls(private_key=private_pem)
457
+
458
+ def get_private_key_bytes(self) -> bytes:
459
+ """
460
+ Get raw private key bytes (32 bytes).
461
+
462
+ Returns:
463
+ Raw private key bytes
464
+
465
+ Raises:
466
+ ValueError: If private key is not available
467
+ """
468
+ if not self._private_key:
469
+ raise ValueError("Private key not available")
470
+
471
+ # Extract raw private key bytes from PEM
472
+ private_bytes = self._private_key.private_bytes(
473
+ encoding=serialization.Encoding.Raw,
474
+ format=serialization.PrivateFormat.Raw,
475
+ encryption_algorithm=serialization.NoEncryption(),
476
+ )
477
+ return private_bytes
478
+
479
+ def get_public_key_bytes(self) -> bytes:
480
+ """
481
+ Get raw public key bytes (32 bytes).
482
+
483
+ Returns:
484
+ Raw public key bytes
485
+
486
+ Raises:
487
+ ValueError: If public key is not available
488
+ """
489
+ if self._public_key:
490
+ public_key = self._public_key
491
+ elif self._private_key:
492
+ public_key = self._private_key.public_key()
493
+ else:
494
+ raise ValueError("No public key available")
495
+
496
+ public_bytes = public_key.public_bytes(
497
+ encoding=serialization.Encoding.Raw,
498
+ format=serialization.PublicFormat.Raw,
499
+ )
500
+ return public_bytes
501
+
502
+ def sign(self, message: Union[str, bytes]) -> str:
503
+ """
504
+ Create Ed25519 signature for message.
505
+
506
+ Args:
507
+ message: Message to sign
508
+
509
+ Returns:
510
+ Base64 encoded signature
511
+
512
+ Raises:
513
+ ValueError: If private key is not available
514
+ """
515
+ if not self._private_key:
516
+ raise ValueError("Private key required for signing")
517
+
518
+ if isinstance(message, str):
519
+ message = message.encode("utf-8")
520
+
521
+ # Ed25519 signing is straightforward, no padding needed
522
+ signature = self._private_key.sign(message)
523
+
524
+ return base64.b64encode(signature).decode("utf-8")
525
+
526
+ def verify(self, message: Union[str, bytes], signature: str) -> bool:
527
+ """
528
+ Verify Ed25519 signature.
529
+
530
+ Args:
531
+ message: Original message
532
+ signature: Base64 encoded signature to verify
533
+
534
+ Returns:
535
+ True if signature is valid, False otherwise
536
+ """
537
+ public_key = self._public_key
538
+ if not public_key and self._private_key:
539
+ public_key = self._private_key.public_key()
540
+
541
+ if not public_key:
542
+ return False
543
+
544
+ try:
545
+ if isinstance(message, str):
546
+ message = message.encode("utf-8")
547
+
548
+ signature_bytes = base64.b64decode(signature.encode("utf-8"))
549
+
550
+ public_key.verify(signature_bytes, message)
551
+ return True
552
+ except Exception:
553
+ return False
554
+
555
+ def sign_detached(self, message: Union[str, bytes]) -> bytes:
556
+ """
557
+ Create detached Ed25519 signature (raw bytes).
558
+
559
+ Args:
560
+ message: Message to sign
561
+
562
+ Returns:
563
+ Raw signature bytes (64 bytes)
564
+
565
+ Raises:
566
+ ValueError: If private key is not available
567
+ """
568
+ if not self._private_key:
569
+ raise ValueError("Private key required for signing")
570
+
571
+ if isinstance(message, str):
572
+ message = message.encode("utf-8")
573
+
574
+ return self._private_key.sign(message)
575
+
576
+ def verify_detached(self, message: Union[str, bytes], signature: bytes) -> bool:
577
+ """
578
+ Verify detached Ed25519 signature.
579
+
580
+ Args:
581
+ message: Original message
582
+ signature: Raw signature bytes to verify
583
+
584
+ Returns:
585
+ True if signature is valid, False otherwise
586
+ """
587
+ public_key = self._public_key
588
+ if not public_key and self._private_key:
589
+ public_key = self._private_key.public_key()
590
+
591
+ if not public_key:
592
+ return False
593
+
594
+ try:
595
+ if isinstance(message, str):
596
+ message = message.encode("utf-8")
597
+
598
+ public_key.verify(signature, message)
599
+ return True
600
+ except Exception:
601
+ return False
602
+
603
+
466
604
  def generate_random_key(length: int = 32) -> str:
467
605
  """
468
606
  Generate a random key for symmetric encryption.
@@ -534,29 +672,31 @@ def verify_password(password: str, hashed_password: str, salt: str) -> bool:
534
672
  return False
535
673
 
536
674
 
537
- def create_symmetric_encryption(
538
- key: Optional[bytes] = None,
539
- ) -> Union[SymmetricEncryption, SimpleSymmetricEncryption]:
675
+ def create_symmetric_encryption(key: Optional[bytes] = None) -> SymmetricEncryption:
540
676
  """
541
- Create a symmetric encryption instance using the best available method.
677
+ Create a symmetric encryption instance.
542
678
 
543
679
  Args:
544
680
  key: Encryption key. If None, generates a new key.
545
681
 
546
682
  Returns:
547
- SymmetricEncryption if cryptography is available, SimpleSymmetricEncryption otherwise
683
+ SymmetricEncryption instance
684
+
685
+ Raises:
686
+ ImportError: If cryptography package is not available
548
687
  """
549
- if HAS_CRYPTOGRAPHY:
550
- return SymmetricEncryption(key)
551
- else:
552
- return SimpleSymmetricEncryption(key)
688
+ if not HAS_CRYPTOGRAPHY:
689
+ raise ImportError(
690
+ "cryptography package is required for encryption. Install it with: uv add cryptography"
691
+ )
692
+ return SymmetricEncryption(key)
553
693
 
554
694
 
555
695
  def encrypt_data(
556
696
  data: Union[str, bytes], key: Optional[bytes] = None
557
697
  ) -> tuple[bytes, bytes]:
558
698
  """
559
- Encrypt data using the best available symmetric encryption.
699
+ Encrypt data using symmetric encryption.
560
700
 
561
701
  Args:
562
702
  data: Data to encrypt
@@ -564,6 +704,9 @@ def encrypt_data(
564
704
 
565
705
  Returns:
566
706
  Tuple of (encrypted_data, key_used)
707
+
708
+ Raises:
709
+ ImportError: If cryptography package is not available
567
710
  """
568
711
  encryptor = create_symmetric_encryption(key)
569
712
  encrypted = encryptor.encrypt(data)
@@ -572,7 +715,7 @@ def encrypt_data(
572
715
 
573
716
  def decrypt_data(encrypted_data: bytes, key: bytes) -> bytes:
574
717
  """
575
- Decrypt data using the best available symmetric encryption.
718
+ Decrypt data using symmetric encryption.
576
719
 
577
720
  Args:
578
721
  encrypted_data: Encrypted data
@@ -580,6 +723,9 @@ def decrypt_data(encrypted_data: bytes, key: bytes) -> bytes:
580
723
 
581
724
  Returns:
582
725
  Decrypted data
726
+
727
+ Raises:
728
+ ImportError: If cryptography package is not available
583
729
  """
584
730
  encryptor = create_symmetric_encryption(key)
585
731
  return encryptor.decrypt(encrypted_data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastlib-py
3
- Version: 0.2.6
3
+ Version: 0.2.7
4
4
  Summary: Fastlib, high performance, easy to use, fast to code, ready for production
5
5
  Author-email: tyvekZhang <tyvekzhang@gmail.com>
6
6
  Maintainer-email: tyvekZhang <tyvekzhang@gmail.com>
@@ -1,126 +1,127 @@
1
- [project]
2
- name = "fastlib-py"
3
- version = "0.2.6"
4
- description = "Fastlib, high performance, easy to use, fast to code, ready for production"
5
- readme = "README.md"
6
- keywords = [
7
- "fastapi",
8
- "library",
9
- "sqlmodel",
10
- "web",
11
- ]
12
- license = "MIT"
13
- maintainers = [
14
- { name = "tyvekZhang", email = "tyvekzhang@gmail.com" },
15
- ]
16
- authors = [
17
- { name = "tyvekZhang", email = "tyvekzhang@gmail.com" },
18
- ]
19
- requires-python = ">=3.9"
20
- classifiers = [
21
- "Development Status :: 4 - Beta",
22
- "Programming Language :: Python",
23
- "Programming Language :: Python :: 3 :: Only",
24
- "Programming Language :: Python :: 3.9",
25
- "Programming Language :: Python :: 3.10",
26
- "Programming Language :: Python :: 3.11",
27
- "Programming Language :: Python :: 3.12",
28
- "Programming Language :: Python :: 3.13",
29
- "Programming Language :: Python :: Implementation :: CPython",
30
- "Programming Language :: Python :: Implementation :: PyPy",
31
- ]
32
- dependencies = [
33
- "aiosqlite>=0.21",
34
- "alembic>=1.15.2",
35
- "asgi-correlation-id>=4.3.4",
36
- "cryptography>=45.0.6",
37
- "diskcache>=5.6.3",
38
- "fastapi[standard]>=0.115.12",
39
- "loguru>=0.7.3",
40
- "openpyxl>=3.1.5",
41
- "pandas>=2.2.3",
42
- "passlib[bcrypt]>=1.7.4",
43
- "pyjwt>=2.10.1",
44
- "sqlmodel>=0.0.24",
45
- ]
46
-
47
- urls.Homepage = "https://github.com/tyvekzhang/fastlib"
48
-
49
- [dependency-groups]
50
- dev = [
51
- "coverage>=7.8",
52
- "httpx>=0.28.1",
53
- "pre-commit>=4.2",
54
- "pytest>=8.3.5",
55
- "pytest-asyncio>=1.1",
56
- ]
57
-
58
- docs = [
59
- "sphinx>=7.2.6",
60
- "sphinx-rtd-theme>=2",
61
- ]
62
-
63
- [tool.ruff]
64
- line-length = 88
65
-
66
- include = [ "fastlib/**/*.py", "main.py" ]
67
- exclude = [ "doc/**/*.py" ]
68
-
69
- lint.select = [
70
- "B", # flake8-bugbear
71
- "C4", # flake8-comprehensions
72
- "E", # pycodestyle errors
73
- "F", # pyflakes
74
- "I", # isort
75
- "UP", # pyupgrade
76
- "W", # pycodestyle warnings
77
- ]
78
- lint.ignore = [
79
- "B008", # do not perform function calls in argument defaults
80
- "C901", # too complex
81
- "E501", # line too long, handled by black
82
- "W191", # indentation contains tabs
83
- ]
84
- lint.isort.known-third-party = [
85
- "aiosqlite",
86
- "alembic",
87
- "diskcache",
88
- "fastapi",
89
- "loguru",
90
- "openpyxl",
91
- "pandas",
92
- "passlib",
93
- "pyjwt",
94
- "sqlmodel",
95
- "starlette",
96
- "pydantic",
97
- "bcrypt",
98
- "cryptography",
99
- ]
100
- # Preserve types, even if a file imports `from __future__ import annotations`.
101
- lint.pyupgrade.keep-runtime-typing = true
102
-
103
- [tool.coverage.run]
104
- parallel = true
105
- data_file = "coverage/.coverage"
106
- source = [ "src/main/app", "src/tests", "main.py" ]
107
- context = '${CONTEXT}'
108
- dynamic_context = "test_function"
109
- omit = [
110
- ]
111
-
112
- [tool.coverage.report]
113
- fail_under = 90
114
- show_missing = true
115
- sort = "-Cover"
116
-
117
- [tool.coverage.html]
118
- show_contexts = true
119
-
120
- [tool.mypy]
121
- strict = true
122
-
123
- [[tool.uv.index]]
124
- name = "tsinghua"
125
- url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
126
- default = true
1
+ [project]
2
+ name = "fastlib-py"
3
+ version = "0.2.7"
4
+ description = "Fastlib, high performance, easy to use, fast to code, ready for production"
5
+ readme = "README.md"
6
+ keywords = [
7
+ "fastapi",
8
+ "library",
9
+ "sqlmodel",
10
+ "web",
11
+ ]
12
+ license = "MIT"
13
+ maintainers = [
14
+ { name = "tyvekZhang", email = "tyvekzhang@gmail.com" },
15
+ ]
16
+ authors = [
17
+ { name = "tyvekZhang", email = "tyvekzhang@gmail.com" },
18
+ ]
19
+ requires-python = ">=3.9"
20
+ classifiers = [
21
+ "Development Status :: 4 - Beta",
22
+ "Programming Language :: Python",
23
+ "Programming Language :: Python :: 3 :: Only",
24
+ "Programming Language :: Python :: 3.9",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ "Programming Language :: Python :: 3.13",
29
+ "Programming Language :: Python :: Implementation :: CPython",
30
+ "Programming Language :: Python :: Implementation :: PyPy",
31
+ ]
32
+ dependencies = [
33
+ "aiosqlite>=0.21",
34
+ "alembic>=1.15.2",
35
+ "asgi-correlation-id>=4.3.4",
36
+ "cryptography>=45.0.6",
37
+ "diskcache>=5.6.3",
38
+ "fastapi[standard]>=0.115.12",
39
+ "loguru>=0.7.3",
40
+ "openpyxl>=3.1.5",
41
+ "pandas>=2.2.3",
42
+ "passlib[bcrypt]>=1.7.4",
43
+ "pyjwt>=2.10.1",
44
+ "sqlmodel>=0.0.24",
45
+ ]
46
+
47
+ urls.Homepage = "https://github.com/tyvekzhang/fastlib"
48
+
49
+ [dependency-groups]
50
+ dev = [
51
+ "coverage>=7.8",
52
+ "httpx>=0.28.1",
53
+ "pre-commit>=4.2",
54
+ "pytest>=8.3.5",
55
+ "pytest-asyncio>=1.1",
56
+ ]
57
+
58
+ docs = [
59
+ "sphinx>=7.2.6",
60
+ "sphinx-rtd-theme>=2",
61
+ ]
62
+
63
+ [tool.ruff]
64
+ line-length = 88
65
+
66
+ include = [ "fastlib/**/*.py", "main.py" ]
67
+ exclude = [ "doc/**/*.py" ]
68
+
69
+ lint.select = [
70
+ "B", # flake8-bugbear
71
+ "C4", # flake8-comprehensions
72
+ "E", # pycodestyle errors
73
+ "F", # pyflakes
74
+ "I", # isort
75
+ "UP", # pyupgrade
76
+ "W", # pycodestyle warnings
77
+ ]
78
+ lint.ignore = [
79
+ "B008", # do not perform function calls in argument defaults
80
+ "C901", # too complex
81
+ "E501", # line too long, handled by black
82
+ "W191", # indentation contains tabs
83
+ ]
84
+ lint.isort.known-third-party = [
85
+ "aiosqlite",
86
+ "alembic",
87
+ "diskcache",
88
+ "fastapi",
89
+ "loguru",
90
+ "openpyxl",
91
+ "pandas",
92
+ "passlib",
93
+ "pyjwt",
94
+ "sqlmodel",
95
+ "starlette",
96
+ "pydantic",
97
+ "bcrypt",
98
+ "cryptography",
99
+ ]
100
+ # Preserve types, even if a file imports `from __future__ import annotations`.
101
+ lint.pyupgrade.keep-runtime-typing = true
102
+
103
+ [tool.coverage.run]
104
+ data_file = "coverage/.coverage"
105
+ source = [ "fastlib" ]
106
+ context = '${CONTEXT}'
107
+ dynamic_context = "test_function"
108
+ omit = [
109
+ ]
110
+
111
+ [tool.coverage.report]
112
+ fail_under = 90
113
+ show_missing = true
114
+ sort = "-Cover"
115
+
116
+ [tool.coverage.html]
117
+ show_contexts = true
118
+ directory = "coverage/html"
119
+ title = "FastLib Coverage Report"
120
+
121
+ [tool.mypy]
122
+ strict = true
123
+
124
+ [[tool.uv.index]]
125
+ name = "tsinghua"
126
+ url = "https://pypi.tuna.tsinghua.edu.cn/simple/"
127
+ default = true
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes