satif-ai 0.2.8__py3-none-any.whl → 0.2.10__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.
satif_ai/utils/zip.py ADDED
@@ -0,0 +1,120 @@
1
+ import asyncio
2
+ import logging
3
+ import zipfile
4
+ from pathlib import Path
5
+ from typing import List, Tuple
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+ # Constants for ZIP file processing, kept local to this utility or passed as args if needed
10
+ _IGNORED_ZIP_MEMBER_PREFIXES = ("__MACOSX/",)
11
+ _IGNORED_ZIP_FILENAME_PREFIXES = ("._",)
12
+ _IGNORED_ZIP_FILENAMES = (".DS_Store",)
13
+
14
+
15
+ async def extract_zip_archive_async(
16
+ zip_path: Path,
17
+ extract_to: Path,
18
+ ignored_member_prefixes: Tuple[str, ...] = _IGNORED_ZIP_MEMBER_PREFIXES,
19
+ ignored_filename_prefixes: Tuple[str, ...] = _IGNORED_ZIP_FILENAME_PREFIXES,
20
+ ignored_filenames: Tuple[str, ...] = _IGNORED_ZIP_FILENAMES,
21
+ ) -> List[Path]:
22
+ """
23
+ Asynchronously extracts a ZIP archive to a specified directory, filtering out ignored files.
24
+
25
+ Args:
26
+ zip_path: Path to the ZIP archive.
27
+ extract_to: Directory where the contents will be extracted.
28
+ ignored_member_prefixes: Tuple of member path prefixes to ignore.
29
+ ignored_filename_prefixes: Tuple of filename prefixes to ignore.
30
+ ignored_filenames: Tuple of exact filenames to ignore.
31
+
32
+ Returns:
33
+ A list of paths to the successfully extracted files.
34
+
35
+ Raises:
36
+ ValueError: If the zip_path is invalid or corrupted.
37
+ RuntimeError: If any other error occurs during extraction.
38
+ """
39
+
40
+ def blocking_extract() -> List[Path]:
41
+ extracted_file_paths = []
42
+ logger.info(f"Extracting ZIP archive '{zip_path.name}' to '{extract_to}'...")
43
+ try:
44
+ extract_to.mkdir(
45
+ parents=True, exist_ok=True
46
+ ) # Ensure extract_to directory exists
47
+
48
+ with zipfile.ZipFile(zip_path, "r") as zip_ref:
49
+ # Security: Preliminary check for unsafe paths before extraction
50
+ for member_name in zip_ref.namelist():
51
+ if member_name.startswith(("/", "..")):
52
+ logger.error(
53
+ f"Skipping potentially unsafe path in ZIP: {member_name}"
54
+ )
55
+ # Depending on security policy, might raise an error here
56
+ continue
57
+
58
+ # Extract all members
59
+ zip_ref.extractall(extract_to)
60
+
61
+ # After extractall, collect all *file* paths, applying filters
62
+ # This second pass of filtering ensures that even if extractall creates them,
63
+ # we don't return paths to ignored files.
64
+ for root, _, files in extract_to.walk():
65
+ for filename in files:
66
+ full_path = root / filename
67
+ # Create a path relative to 'extract_to' to check against member prefixes
68
+ # This ensures that '__MACOSX/file.txt' is correctly ignored,
69
+ # not just a top-level '__MACOSX' directory.
70
+ try:
71
+ relative_path_to_check = full_path.relative_to(extract_to)
72
+ except ValueError:
73
+ # This can happen if full_path is not under extract_to,
74
+ # which ideally shouldn't occur if zip_ref.extractall worked as expected
75
+ # and target_path checks were effective.
76
+ logger.warning(
77
+ f"File {full_path} seems to be outside extraction root {extract_to}. Skipping."
78
+ )
79
+ continue
80
+
81
+ path_str_to_check_prefixes = str(relative_path_to_check)
82
+
83
+ if not (
84
+ any(
85
+ path_str_to_check_prefixes.startswith(p)
86
+ for p in ignored_member_prefixes
87
+ )
88
+ or any(
89
+ full_path.name.startswith(p)
90
+ for p in ignored_filename_prefixes
91
+ )
92
+ or full_path.name in ignored_filenames
93
+ ):
94
+ extracted_file_paths.append(full_path)
95
+ else:
96
+ logger.debug(f"Ignoring file post-extraction: {full_path}")
97
+
98
+ if not extracted_file_paths:
99
+ logger.warning(
100
+ f"ZIP archive '{zip_path.name}' is empty or contains no processable files after filtering."
101
+ )
102
+ else:
103
+ logger.info(
104
+ f"Successfully extracted {len(extracted_file_paths)} file(s) from '{zip_path.name}'."
105
+ )
106
+ return extracted_file_paths
107
+ except zipfile.BadZipFile as e:
108
+ logger.error(
109
+ f"Invalid or corrupted ZIP file: {zip_path.name}", exc_info=True
110
+ )
111
+ raise ValueError(f"Invalid or corrupted ZIP file: {zip_path.name}") from e
112
+ except Exception as e:
113
+ logger.error(
114
+ f"Failed to extract ZIP archive '{zip_path.name}': {e}", exc_info=True
115
+ )
116
+ raise RuntimeError(
117
+ f"Unexpected error during ZIP extraction for '{zip_path.name}'"
118
+ ) from e
119
+
120
+ return await asyncio.to_thread(blocking_extract)
@@ -1,17 +1,19 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: satif-ai
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: AI Agents for Satif
5
5
  License: MIT
6
- Author: Bryan Djafer
7
- Author-email: bryan.djafer@syncpulse.fr
8
- Requires-Python: >=3.10,<4.0
6
+ Author: Syncpulse
7
+ Maintainer: Bryan Djafer
8
+ Maintainer-email: bryan.djafer@syncpulse.fr
9
+ Requires-Python: >=3.10,<3.14
9
10
  Classifier: License :: OSI Approved :: MIT License
10
11
  Classifier: Programming Language :: Python :: 3
11
12
  Classifier: Programming Language :: Python :: 3.10
12
13
  Classifier: Programming Language :: Python :: 3.11
13
14
  Classifier: Programming Language :: Python :: 3.12
14
15
  Classifier: Programming Language :: Python :: 3.13
16
+ Provides-Extra: xlsx
15
17
  Requires-Dist: openai-agents (>=0.0.9,<0.0.10)
16
18
  Requires-Dist: satif-sdk (>=0.1.0,<1.0.0)
17
19
  Requires-Dist: sdif-mcp (>=0.1.0,<1.0.0)
@@ -0,0 +1,20 @@
1
+ satif_ai/__init__.py,sha256=cqJ6Kd9IolVodPi9yOBPnfhYQXH5a1JgRB3HfLOtP_4,611
2
+ satif_ai/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ satif_ai/adapters/tidy.py,sha256=1g7Wcq8agAZhaAqQDhhD8yh3iO5gZ4mwdKHsiNN3hHY,18540
4
+ satif_ai/standardize.py,sha256=TgAB_nhcHY8zqlfT1PpgfgSswqdE-ly-dheQz-7NC7Q,5674
5
+ satif_ai/standardizers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ satif_ai/standardizers/ai.py,sha256=jtYM-ChjLtkpFaubz980CTCNAoC33iYxB3pq0_hn2lU,21045
7
+ satif_ai/standardizers/ai_csv.py,sha256=LbCRaLleujQRgSRRyt9ujbED-PIGRq1J8zRnejGM5nc,25437
8
+ satif_ai/standardizers/ai_xlsx.py,sha256=558Bzfy8WGuk5mdnjMvvtakQXcU3rmwK3ykPjpXKwmQ,15863
9
+ satif_ai/transform.py,sha256=g5XNeVCIKUgDW3UIhf02MN9xkXnWF3EJXS0Eig_hfD8,7677
10
+ satif_ai/transformation_builders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ satif_ai/transformation_builders/syncpulse.py,sha256=c59BZicNnqs3NDKpflBAPqw42pGb6nYB2Zps0ChGyaM,11368
12
+ satif_ai/utils/__init__.py,sha256=F-usaCt_vX872mXvtukuZdNMPnkVqDb8RaDgox2uow4,212
13
+ satif_ai/utils/merge_sdif.py,sha256=y4C6pgkdyer0QugroFKUck4Eud4Ap-tJzM-eclMo3Rw,25629
14
+ satif_ai/utils/openai_mcp.py,sha256=duCQZXG0mBs9DOOFIUvzraJhxD2IDzegWO9iOiLfFwY,3938
15
+ satif_ai/utils/zip.py,sha256=G_GK8629Iw0TLFCQJfnqOscv7MoKF5zdzxvEAbL7Gss,5186
16
+ satif_ai-0.2.10.dist-info/LICENSE,sha256=kS8EN6yAaGZd7V5z6GKSn_x3ozcZltrfRky4vMPRCw8,1072
17
+ satif_ai-0.2.10.dist-info/METADATA,sha256=O5QWv8YJFtB5AIniv0LRgmSgpEaRLVdlz8WHZAru1X8,719
18
+ satif_ai-0.2.10.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
19
+ satif_ai-0.2.10.dist-info/entry_points.txt,sha256=Mz2SwYALjktap1bF-Q3EWBgiZVNT6QJCVsCs_fCV33Y,43
20
+ satif_ai-0.2.10.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- from satif_core.code_builders.base import AsyncCodeBuilder, CodeBuilder
2
-
3
-
4
- class AdaptationCodeBuilder(CodeBuilder):
5
- pass
6
-
7
-
8
- class AdaptationAsyncCodeBuilder(AsyncCodeBuilder):
9
- pass
@@ -1,13 +0,0 @@
1
- satif_ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- satif_ai/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- satif_ai/adapters/tidy.py,sha256=2oYj7Gz3vOQtzcpoJI4JbftWlMKvOWL8rdwthjg-zUE,19884
4
- satif_ai/code_builders/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- satif_ai/code_builders/adaptation.py,sha256=E29YM0S6pMtAfB0uzSUexoeWKwXfF8iJVyYUCKWQz5k,188
6
- satif_ai/code_builders/transformation.py,sha256=5B7a6lDv-gqQo83F8fQeSw2gHpDgznoDfjXsASkLc60,11870
7
- satif_ai/standardizers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- satif_ai/standardizers/ai_csv.py,sha256=c0CKnIib610GgwGqaF8NaqT_P4pZ2BupO-BTSNuIhoc,25385
9
- satif_ai-0.2.8.dist-info/LICENSE,sha256=kS8EN6yAaGZd7V5z6GKSn_x3ozcZltrfRky4vMPRCw8,1072
10
- satif_ai-0.2.8.dist-info/METADATA,sha256=CZBbNd1A-KL8eoOzmmz7bW3ue4HGOC2Qic60wQ-v6z8,670
11
- satif_ai-0.2.8.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
12
- satif_ai-0.2.8.dist-info/entry_points.txt,sha256=Mz2SwYALjktap1bF-Q3EWBgiZVNT6QJCVsCs_fCV33Y,43
13
- satif_ai-0.2.8.dist-info/RECORD,,