mpatch 1.6.4__cp38-abi3-win_amd64.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.
mpatch/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ from .mpatch import *
2
+
3
+ __doc__ = mpatch.__doc__
4
+ if hasattr(mpatch, "__all__"):
5
+ __all__ = mpatch.__all__
mpatch/__init__.pyi ADDED
@@ -0,0 +1,554 @@
1
+ # ruff: noqa: E501
2
+ import os
3
+ import pathlib
4
+ from typing import Any, Iterator, overload
5
+
6
+ class MpatchError(Exception): ...
7
+ class ParseError(MpatchError): ...
8
+ class ApplyError(MpatchError): ...
9
+ class PathTraversalError(ApplyError): ...
10
+
11
+ VERSION: str
12
+
13
+ class Hunk:
14
+ """Represents a single hunk of changes within a patch."""
15
+ def __init__(
16
+ self,
17
+ lines: list[str],
18
+ *,
19
+ old_start_line: int | None = None,
20
+ new_start_line: int | None = None,
21
+ ) -> None:
22
+ """
23
+ Creates a new Hunk.
24
+
25
+ Args:
26
+ lines (list[str]): The raw lines of the hunk, each prefixed with ' ', '+', or '-'.
27
+ old_start_line (int | None, optional): The starting line number in the original file. Defaults to None.
28
+ new_start_line (int | None, optional): The starting line number in the new file. Defaults to None.
29
+ """
30
+ ...
31
+ @property
32
+ def lines(self) -> list[str]:
33
+ """The raw lines of the hunk, each prefixed with ' ', '+', or '-'."""
34
+ ...
35
+ @property
36
+ def old_start_line(self) -> int | None:
37
+ """The starting line number in the original file (1-based)."""
38
+ ...
39
+ @property
40
+ def new_start_line(self) -> int | None:
41
+ """The starting line number in the new file (1-based)."""
42
+ ...
43
+ @property
44
+ def context_lines(self) -> list[str]:
45
+ """Extracts the context lines from the hunk (lines starting with ' ')."""
46
+ ...
47
+ @property
48
+ def added_lines(self) -> list[str]:
49
+ """Extracts the added lines from the hunk (lines starting with '+')."""
50
+ ...
51
+ @property
52
+ def removed_lines(self) -> list[str]:
53
+ """Extracts the removed lines from the hunk (lines starting with '-')."""
54
+ ...
55
+ @property
56
+ def match_block(self) -> list[str]:
57
+ """Extracts the lines that need to be matched in the target file."""
58
+ ...
59
+ @property
60
+ def replace_block(self) -> list[str]:
61
+ """Extracts the lines that will replace the matched block in the target file."""
62
+ ...
63
+ @property
64
+ def has_changes(self) -> bool:
65
+ """Checks if the hunk contains any effective changes (additions or
66
+ deletions).
67
+ """
68
+ ...
69
+ def invert(self) -> Hunk:
70
+ """Creates a new Hunk that reverses the changes in this one."""
71
+ ...
72
+ def __len__(self) -> int: ...
73
+ @overload
74
+ def __getitem__(self, idx: int) -> str: ...
75
+ @overload
76
+ def __getitem__(self, idx: slice) -> list[str]: ...
77
+ def __iter__(self) -> Iterator[str]: ...
78
+ def __bool__(self) -> bool: ...
79
+ def __invert__(self) -> Hunk: ...
80
+ def __str__(self) -> str: ...
81
+ def __repr__(self) -> str: ...
82
+
83
+ class Patch:
84
+ """Represents all the changes to be applied to a single file."""
85
+ def __init__(
86
+ self,
87
+ file_path: str | os.PathLike[Any],
88
+ hunks: list[Hunk],
89
+ *,
90
+ ends_with_newline: bool = True,
91
+ ) -> None:
92
+ """
93
+ Creates a new Patch.
94
+
95
+ Args:
96
+ file_path (str | os.PathLike): The relative path of the file to be patched.
97
+ hunks (list[Hunk]): A list of hunks to be applied to the file.
98
+ ends_with_newline (bool, optional): Indicates whether the file should end with a newline. Defaults to True.
99
+ """
100
+ ...
101
+ @classmethod
102
+ def from_texts(
103
+ cls,
104
+ file_path: str | os.PathLike[Any],
105
+ old_text: str,
106
+ new_text: str,
107
+ *,
108
+ context_len: int = 3,
109
+ ) -> Patch:
110
+ """
111
+ Creates a `Patch` object by comparing two texts.
112
+
113
+ This function compares `old_text` and `new_text`, generates a unified diff,
114
+ and parses it into a structured `Patch` object.
115
+
116
+ Args:
117
+ file_path (str | os.PathLike): The file path to associate with the patch.
118
+ old_text (str): The original text content.
119
+ new_text (str): The new, modified text content.
120
+ context_len (int, optional): The number of context lines to include.
121
+ Defaults to 3.
122
+
123
+ Returns:
124
+ Patch: The generated patch object.
125
+ """
126
+ ...
127
+ @property
128
+ def file_path(self) -> pathlib.Path:
129
+ """The relative path of the file to be patched."""
130
+ ...
131
+ @file_path.setter
132
+ def file_path(self, path: str | os.PathLike[Any]) -> None: ...
133
+ @property
134
+ def hunks(self) -> list[Hunk]:
135
+ """A list of hunks to be applied to the file."""
136
+ ...
137
+ @property
138
+ def ends_with_newline(self) -> bool:
139
+ """Indicates whether the file should end with a newline."""
140
+ ...
141
+ @property
142
+ def is_creation(self) -> bool:
143
+ """Checks if the patch represents a file creation."""
144
+ ...
145
+ @property
146
+ def is_deletion(self) -> bool:
147
+ """Checks if the patch represents a full file deletion."""
148
+ ...
149
+ def invert(self) -> Patch:
150
+ """Creates a new Patch that reverses the changes in this one."""
151
+ ...
152
+ def apply_to_file(
153
+ self,
154
+ target_dir: str | os.PathLike[Any],
155
+ *,
156
+ fuzz_factor: float = 0.7,
157
+ dry_run: bool = False,
158
+ ) -> PatchResult:
159
+ """
160
+ Applies the patch to a file on disk (OOP alias for `apply_patch_to_file`).
161
+
162
+ Args:
163
+ target_dir (str | os.PathLike): The base directory to apply the patch.
164
+ fuzz_factor (float, optional): Similarity threshold (0.0 to 1.0). Default is 0.7.
165
+ dry_run (bool, optional): If True, previews changes without writing to disk. Default is False.
166
+
167
+ Returns:
168
+ PatchResult: The result of the application.
169
+ """
170
+ ...
171
+ def apply_to_content(
172
+ self,
173
+ original: str | None = None,
174
+ *,
175
+ fuzz_factor: float = 0.7,
176
+ dry_run: bool = False,
177
+ ) -> InMemoryResult:
178
+ """
179
+ Applies the patch to a string in memory (OOP alias for `apply_patch_to_content`).
180
+
181
+ Args:
182
+ original (str | None, optional): The original content. None for file creation. Defaults to None.
183
+ fuzz_factor (float, optional): Similarity threshold (0.0 to 1.0). Default is 0.7.
184
+ dry_run (bool, optional): If True, previews changes without writing. Default is False.
185
+
186
+ Returns:
187
+ InMemoryResult: The result of the application.
188
+ """
189
+ ...
190
+ def __len__(self) -> int: ...
191
+ @overload
192
+ def __getitem__(self, idx: int) -> Hunk: ...
193
+ @overload
194
+ def __getitem__(self, idx: slice) -> list[Hunk]: ...
195
+ def __iter__(self) -> Iterator[Hunk]: ...
196
+ def __bool__(self) -> bool: ...
197
+ def __invert__(self) -> Patch: ...
198
+ def __str__(self) -> str: ...
199
+ def __repr__(self) -> str: ...
200
+
201
+ class HunkFailure:
202
+ """Provides detailed reasons for hunk failures in applying patches."""
203
+ @property
204
+ def hunk_index(self) -> int:
205
+ """The 1-based index of the hunk that failed."""
206
+ ...
207
+ @property
208
+ def reason(self) -> str:
209
+ """The reason for the failure."""
210
+ ...
211
+ @property
212
+ def error_type(self) -> str:
213
+ """The specific error type (e.g. 'ContextNotFound',
214
+ 'FuzzyMatchBelowThreshold').
215
+ """
216
+ ...
217
+ @property
218
+ def best_score(self) -> float | None:
219
+ """The best score found, if the error was a fuzzy match failure."""
220
+ ...
221
+ @property
222
+ def threshold(self) -> float | None:
223
+ """The threshold that was not met, if the error was a fuzzy match failure."""
224
+ ...
225
+ @property
226
+ def ambiguous_matches(self) -> list[int] | None:
227
+ """The ambiguous line indices found, if the error was due to an ambiguous
228
+ match.
229
+ """
230
+ ...
231
+ def __repr__(self) -> str: ...
232
+
233
+ class HunkApplyStatus:
234
+ """Status of a single hunk's application attempt."""
235
+ @property
236
+ def status(self) -> str:
237
+ """The status: 'Applied', 'Skipped', or 'Failed'."""
238
+ ...
239
+ @property
240
+ def location_start(self) -> int | None:
241
+ """The starting line index in the target file where the hunk was applied."""
242
+ ...
243
+ @property
244
+ def location_length(self) -> int | None:
245
+ """The number of lines in the target file that were replaced."""
246
+ ...
247
+ @property
248
+ def match_type(self) -> str | None:
249
+ """The type of match used ('Exact', 'ExactIgnoringWhitespace', or 'Fuzzy')."""
250
+ ...
251
+ @property
252
+ def replaced_lines(self) -> list[str] | None:
253
+ """The original lines from the target file that were replaced."""
254
+ ...
255
+ @property
256
+ def error_reason(self) -> str | None:
257
+ """The error reason if the status is 'Failed'."""
258
+ ...
259
+
260
+ class ApplyResult:
261
+ """Status report detailing the applied success of hunks."""
262
+ @property
263
+ def all_applied_cleanly(self) -> bool:
264
+ """True if all hunks in the patch were applied successfully or skipped."""
265
+ ...
266
+ @property
267
+ def has_failures(self) -> bool:
268
+ """True if any hunk in the patch failed to apply."""
269
+ ...
270
+ @property
271
+ def failure_count(self) -> int:
272
+ """The number of hunks that failed to apply."""
273
+ ...
274
+ @property
275
+ def success_count(self) -> int:
276
+ """The number of hunks that were applied successfully or skipped."""
277
+ ...
278
+ @property
279
+ def failures(self) -> list[HunkFailure]:
280
+ """A list of all hunks that failed to apply."""
281
+ ...
282
+ @property
283
+ def hunk_results(self) -> list[HunkApplyStatus]:
284
+ """The detailed status for every hunk in the patch."""
285
+ ...
286
+ def __len__(self) -> int: ...
287
+ @overload
288
+ def __getitem__(self, idx: int) -> HunkApplyStatus: ...
289
+ @overload
290
+ def __getitem__(self, idx: slice) -> list[HunkApplyStatus]: ...
291
+ def __iter__(self) -> Iterator[HunkApplyStatus]: ...
292
+ def __bool__(self) -> bool: ...
293
+
294
+ class InMemoryResult:
295
+ """Represents the success result of an internal string apply logic."""
296
+ @property
297
+ def new_content(self) -> str:
298
+ """The new content after applying the patch."""
299
+ ...
300
+ @property
301
+ def report(self) -> ApplyResult:
302
+ """Detailed results for each hunk within the patch operation."""
303
+ ...
304
+ def __bool__(self) -> bool: ...
305
+
306
+ class PatchResult:
307
+ """Represents a single File I/O apply result."""
308
+ @property
309
+ def report(self) -> ApplyResult:
310
+ """Detailed results for each hunk within the patch operation."""
311
+ ...
312
+ @property
313
+ def diff(self) -> str | None:
314
+ """The unified diff of the proposed changes (only populated when dry_run
315
+ is true).
316
+ """
317
+ ...
318
+ def __bool__(self) -> bool: ...
319
+
320
+ class BatchResult:
321
+ """Result from batch-applying patches to a directory on disk."""
322
+ @property
323
+ def all_succeeded(self) -> bool:
324
+ """True if all patches in the batch were applied without hard errors."""
325
+ ...
326
+ @property
327
+ def hard_failures(self) -> list[tuple[str, str]]:
328
+ """A list of operations that resulted in a hard error."""
329
+ ...
330
+ @property
331
+ def results(self) -> dict[str, PatchResult | str]:
332
+ """A dict of results for each patch operation attempted."""
333
+ ...
334
+ def __len__(self) -> int: ...
335
+ def __getitem__(self, key: str) -> PatchResult | str: ...
336
+ def __contains__(self, key: str) -> bool: ...
337
+ def __bool__(self) -> bool: ...
338
+
339
+ def detect_patch(diff: str) -> str:
340
+ """
341
+ Automatically detects the format of the input text.
342
+
343
+ Args:
344
+ diff (str): The patch content.
345
+
346
+ Returns:
347
+ str: 'Markdown', 'Unified', 'Conflict', or 'Unknown'.
348
+ """
349
+ ...
350
+
351
+ def parse_auto(diff: str) -> list[Patch]:
352
+ """
353
+ Automatically detects the format of the input text and parses it into a list
354
+ of patches.
355
+
356
+ Args:
357
+ diff (str): The patch content (Markdown, Unified, or Conflict Markers).
358
+
359
+ Returns:
360
+ list[Patch]: A list of parsed patches.
361
+ """
362
+ ...
363
+
364
+ def parse_diffs(diff: str) -> list[Patch]:
365
+ """
366
+ Parses a string containing one or more markdown diff blocks into a list of patches.
367
+
368
+ Args:
369
+ diff (str): The markdown content.
370
+
371
+ Returns:
372
+ list[Patch]: A list of parsed patches.
373
+ """
374
+ ...
375
+
376
+ def parse_patches(diff: str) -> list[Patch]:
377
+ """
378
+ Parses a string containing raw unified diff content into a list of patches.
379
+
380
+ Args:
381
+ diff (str): The raw unified diff content.
382
+
383
+ Returns:
384
+ list[Patch]: A list of parsed patches.
385
+ """
386
+ ...
387
+
388
+ def parse_conflict_markers(diff: str) -> list[Patch]:
389
+ """
390
+ Parses a string containing "Conflict Marker" style diffs (<<<<, ====, >>>>).
391
+
392
+ Args:
393
+ diff (str): The conflict marker content.
394
+
395
+ Returns:
396
+ list[Patch]: A list of parsed patches.
397
+ """
398
+ ...
399
+
400
+ def invert_patches(patches: list[Patch]) -> list[Patch]:
401
+ """
402
+ Inverts a list of patches (swaps additions and deletions).
403
+
404
+ Args:
405
+ patches (list[Patch]): The patches to invert.
406
+
407
+ Returns:
408
+ list[Patch]: The inverted patches.
409
+ """
410
+ ...
411
+
412
+ def create_unified_diff(
413
+ file_path: str | os.PathLike[Any],
414
+ old_text: str,
415
+ new_text: str,
416
+ *,
417
+ context_len: int = 3,
418
+ ) -> str:
419
+ """
420
+ Creates a unified diff string by comparing two texts.
421
+
422
+ This function compares `old_text` and `new_text` and generates a standard
423
+ unified diff representation. It is useful for generating patches programmatically
424
+ that can be applied later or displayed to the user.
425
+
426
+ Args:
427
+ file_path (str | os.PathLike): The file path to associate with the patch
428
+ headers.
429
+ old_text (str): The original text content.
430
+ new_text (str): The new, modified text content.
431
+ context_len (int, optional): Number of context lines to include before
432
+ and after changes. Defaults to 3.
433
+
434
+ Returns:
435
+ str: The generated unified diff string.
436
+
437
+ Example:
438
+ >>> import mpatch
439
+ >>> diff = mpatch.create_unified_diff(
440
+ ... "main.py", "print('old')\\n", "print('new')\\n"
441
+ ... )
442
+ >>> print(diff)
443
+ --- a/main.py
444
+ +++ b/main.py
445
+ @@ -1,1 +1,1 @@
446
+ -print('old')
447
+ +print('new')
448
+ """
449
+ ...
450
+
451
+ def patch_content(
452
+ diff: str,
453
+ original: str | None = None,
454
+ *,
455
+ fuzz_factor: float = 0.7,
456
+ dry_run: bool = False,
457
+ ) -> str:
458
+ """
459
+ Applies a diff to a string in memory.
460
+
461
+ Args:
462
+ diff (str): The patch content (Markdown, Unified, or Conflict Markers).
463
+ original (str | None, optional): The original content. None for file creation. Defaults to None.
464
+ fuzz_factor (float, optional): Similarity threshold (0.0 to 1.0). Default is 0.7.
465
+ dry_run (bool, optional): If True, returns what would happen without making changes. Default is False.
466
+
467
+ Returns:
468
+ str: The new patched content.
469
+ """
470
+ ...
471
+
472
+ def apply_directory(
473
+ diff: str,
474
+ target_dir: str | os.PathLike[Any],
475
+ *,
476
+ fuzz_factor: float = 0.7,
477
+ dry_run: bool = False,
478
+ ) -> bool:
479
+ """
480
+ Applies a diff containing multiple patches to a target directory.
481
+
482
+ Args:
483
+ diff (str): The patch content containing one or more file changes.
484
+ target_dir (str | os.PathLike): The base directory to apply the patches.
485
+ fuzz_factor (float, optional): Similarity threshold (0.0 to 1.0). Default is 0.7.
486
+ dry_run (bool, optional): If True, previews changes without writing to disk. Default is False.
487
+
488
+ Returns:
489
+ bool: True if all patches succeeded, False if any hard errors occurred.
490
+ """
491
+ ...
492
+
493
+ def apply_patch_to_content(
494
+ patch: Patch,
495
+ original: str | None = None,
496
+ *,
497
+ fuzz_factor: float = 0.7,
498
+ dry_run: bool = False,
499
+ ) -> InMemoryResult:
500
+ """
501
+ Applies a Patch object to a string content in memory.
502
+
503
+ Args:
504
+ patch (Patch): The patch to apply.
505
+ original (str | None, optional): The original content. None for file creation. Defaults to None.
506
+ fuzz_factor (float, optional): Similarity threshold (0.0 to 1.0). Default is 0.7.
507
+ dry_run (bool, optional): If True, returns what would happen without making changes. Default is False.
508
+
509
+ Returns:
510
+ InMemoryResult: The result of the application.
511
+ """
512
+ ...
513
+
514
+ def apply_patch_to_file(
515
+ patch: Patch,
516
+ target_dir: str | os.PathLike[Any],
517
+ *,
518
+ fuzz_factor: float = 0.7,
519
+ dry_run: bool = False,
520
+ ) -> PatchResult:
521
+ """
522
+ Applies a Patch object to a file on disk.
523
+
524
+ Args:
525
+ patch (Patch): The patch to apply.
526
+ target_dir (str | os.PathLike): The base directory to apply the patch.
527
+ fuzz_factor (float, optional): Similarity threshold (0.0 to 1.0). Default is 0.7.
528
+ dry_run (bool, optional): If True, previews changes without writing to disk. Default is False.
529
+
530
+ Returns:
531
+ PatchResult: The result of the application.
532
+ """
533
+ ...
534
+
535
+ def apply_patches_to_dir(
536
+ patches: list[Patch],
537
+ target_dir: str | os.PathLike[Any],
538
+ *,
539
+ fuzz_factor: float = 0.7,
540
+ dry_run: bool = False,
541
+ ) -> BatchResult:
542
+ """
543
+ Applies a list of patches to a directory on disk.
544
+
545
+ Args:
546
+ patches (list[Patch]): The patches to apply.
547
+ target_dir (str | os.PathLike): The base directory to apply the patches.
548
+ fuzz_factor (float, optional): Similarity threshold (0.0 to 1.0). Default is 0.7.
549
+ dry_run (bool, optional): If True, previews changes without writing to disk. Default is False.
550
+
551
+ Returns:
552
+ BatchResult: The aggregated results of the applications.
553
+ """
554
+ ...
mpatch/mpatch.pyd ADDED
Binary file
mpatch/py.typed ADDED
File without changes