pdd-cli 0.0.12__py3-none-any.whl → 0.0.14__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.
Potentially problematic release.
This version of pdd-cli might be problematic. Click here for more details.
- pdd/cli.py +1 -1
- pdd/crash_main.py +10 -4
- pdd/fix_error_loop.py +74 -60
- {pdd_cli-0.0.12.dist-info → pdd_cli-0.0.14.dist-info}/METADATA +3 -3
- {pdd_cli-0.0.12.dist-info → pdd_cli-0.0.14.dist-info}/RECORD +9 -9
- {pdd_cli-0.0.12.dist-info → pdd_cli-0.0.14.dist-info}/LICENSE +0 -0
- {pdd_cli-0.0.12.dist-info → pdd_cli-0.0.14.dist-info}/WHEEL +0 -0
- {pdd_cli-0.0.12.dist-info → pdd_cli-0.0.14.dist-info}/entry_points.txt +0 -0
- {pdd_cli-0.0.12.dist-info → pdd_cli-0.0.14.dist-info}/top_level.txt +0 -0
pdd/cli.py
CHANGED
|
@@ -46,7 +46,7 @@ console = Console()
|
|
|
46
46
|
@click.option("--review-examples", is_flag=True,
|
|
47
47
|
help="Review and optionally exclude few-shot examples before command execution.")
|
|
48
48
|
@click.option('--local', is_flag=True, help='Run commands locally instead of in the cloud.')
|
|
49
|
-
@click.version_option(version="0.0.
|
|
49
|
+
@click.version_option(version="0.0.14")
|
|
50
50
|
@click.pass_context
|
|
51
51
|
def cli(
|
|
52
52
|
ctx,
|
pdd/crash_main.py
CHANGED
|
@@ -66,7 +66,7 @@ def crash_main(
|
|
|
66
66
|
error_content = input_strings["error_file"]
|
|
67
67
|
|
|
68
68
|
# Get model parameters from context
|
|
69
|
-
strength = ctx.obj.get('strength', 0.
|
|
69
|
+
strength = ctx.obj.get('strength', 0.97)
|
|
70
70
|
temperature = ctx.obj.get('temperature', 0)
|
|
71
71
|
|
|
72
72
|
if loop:
|
|
@@ -98,9 +98,15 @@ def crash_main(
|
|
|
98
98
|
attempts = 1
|
|
99
99
|
|
|
100
100
|
# Determine if contents were actually updated
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
if final_code != "":
|
|
102
|
+
update_code = final_code != code_content
|
|
103
|
+
else:
|
|
104
|
+
update_code = False
|
|
105
|
+
if final_program != "":
|
|
106
|
+
update_program = final_program != program_content
|
|
107
|
+
else:
|
|
108
|
+
update_program = False
|
|
109
|
+
|
|
104
110
|
# Save results if contents changed
|
|
105
111
|
if update_code and output_file_paths.get("output"):
|
|
106
112
|
with open(output_file_paths["output"], 'w') as f:
|
pdd/fix_error_loop.py
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
import os
|
|
3
3
|
import sys
|
|
4
|
-
import re
|
|
5
4
|
import subprocess
|
|
6
5
|
import shutil
|
|
7
6
|
from datetime import datetime
|
|
8
7
|
|
|
8
|
+
# Added for the new pytest-based reporting:
|
|
9
|
+
# import pytest
|
|
10
|
+
# import io
|
|
11
|
+
|
|
9
12
|
from rich import print as rprint
|
|
10
13
|
from rich.console import Console
|
|
11
14
|
|
|
@@ -18,30 +21,59 @@ def escape_brackets(text: str) -> str:
|
|
|
18
21
|
"""Escape square brackets so Rich doesn't misinterpret them."""
|
|
19
22
|
return text.replace("[", "\\[").replace("]", "\\]")
|
|
20
23
|
|
|
21
|
-
def
|
|
24
|
+
def run_pytest_on_file(test_file: str) -> (int, int, int, str):
|
|
22
25
|
"""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
Returns a tuple: (fails, errors, warnings)
|
|
26
|
+
Run pytest on the specified test file using a custom plugin to capture results.
|
|
27
|
+
Returns a tuple: (failures, errors, warnings, logs)
|
|
26
28
|
"""
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
29
|
+
import pytest
|
|
30
|
+
import io
|
|
31
|
+
# import sys
|
|
32
|
+
|
|
33
|
+
class TestResultCollector:
|
|
34
|
+
def __init__(self):
|
|
35
|
+
self.failures = 0
|
|
36
|
+
self.errors = 0
|
|
37
|
+
self.warnings = 0
|
|
38
|
+
self.logs = io.StringIO() # Capture logs in memory
|
|
39
|
+
|
|
40
|
+
def pytest_runtest_logreport(self, report):
|
|
41
|
+
"""Capture test failures and errors"""
|
|
42
|
+
if report.when == "call":
|
|
43
|
+
if report.failed:
|
|
44
|
+
self.failures += 1
|
|
45
|
+
elif report.outcome == "error":
|
|
46
|
+
self.errors += 1
|
|
47
|
+
if report.when == "setup" and report.failed:
|
|
48
|
+
self.errors += 1
|
|
49
|
+
if report.when == "teardown" and report.failed:
|
|
50
|
+
self.errors += 1
|
|
51
|
+
|
|
52
|
+
def pytest_sessionfinish(self, session):
|
|
53
|
+
"""Capture warnings from pytest session"""
|
|
54
|
+
terminal_reporter = session.config.pluginmanager.get_plugin("terminalreporter")
|
|
55
|
+
if terminal_reporter:
|
|
56
|
+
self.warnings = len(terminal_reporter.stats.get("warnings", []))
|
|
57
|
+
|
|
58
|
+
def capture_logs(self):
|
|
59
|
+
"""Redirect stdout and stderr to capture logs"""
|
|
60
|
+
sys.stdout = self.logs
|
|
61
|
+
sys.stderr = self.logs
|
|
62
|
+
|
|
63
|
+
def get_logs(self):
|
|
64
|
+
"""Return captured logs and reset stdout/stderr"""
|
|
65
|
+
sys.stdout = sys.__stdout__
|
|
66
|
+
sys.stderr = sys.__stderr__
|
|
67
|
+
return self.logs.getvalue()
|
|
68
|
+
|
|
69
|
+
collector = TestResultCollector()
|
|
70
|
+
collector.capture_logs()
|
|
71
|
+
try:
|
|
72
|
+
# Run pytest on the given test file.
|
|
73
|
+
pytest.main(["-vv", test_file], plugins=[collector])
|
|
74
|
+
finally:
|
|
75
|
+
logs = collector.get_logs()
|
|
76
|
+
return collector.failures, collector.errors, collector.warnings, logs
|
|
45
77
|
|
|
46
78
|
def fix_error_loop(unit_test_file: str,
|
|
47
79
|
code_file: str,
|
|
@@ -55,10 +87,12 @@ def fix_error_loop(unit_test_file: str,
|
|
|
55
87
|
verbose: bool = False):
|
|
56
88
|
"""
|
|
57
89
|
Attempt to fix errors in a unit test and corresponding code using repeated iterations,
|
|
58
|
-
counting only the number of times we actually call the LLM fix function.
|
|
59
|
-
are re-run in the same iteration after a fix to see if we've succeeded,
|
|
60
|
-
'attempts' matches the number of fix attempts (not the total test runs).
|
|
61
|
-
|
|
90
|
+
counting only the number of times we actually call the LLM fix function.
|
|
91
|
+
The tests are re-run in the same iteration after a fix to see if we've succeeded,
|
|
92
|
+
so that 'attempts' matches the number of fix attempts (not the total test runs).
|
|
93
|
+
|
|
94
|
+
This updated version uses pytest's API directly to retrieve failures, errors, and warnings.
|
|
95
|
+
|
|
62
96
|
Inputs:
|
|
63
97
|
unit_test_file: Path to the file containing unit tests.
|
|
64
98
|
code_file: Path to the file containing the code under test.
|
|
@@ -124,11 +158,9 @@ def fix_error_loop(unit_test_file: str,
|
|
|
124
158
|
with open(error_log_file, "a") as elog:
|
|
125
159
|
elog.write(f"\n{iteration_header}\n")
|
|
126
160
|
|
|
127
|
-
# 1) Run the unit tests
|
|
161
|
+
# 1) Run the unit tests using pytest's API directly.
|
|
128
162
|
try:
|
|
129
|
-
|
|
130
|
-
result = subprocess.run(pytest_cmd, capture_output=True, text=True)
|
|
131
|
-
pytest_output = result.stdout + "\n" + result.stderr
|
|
163
|
+
fails, errors, warnings, pytest_output = run_pytest_on_file(unit_test_file)
|
|
132
164
|
except Exception as e:
|
|
133
165
|
rprint(f"[red]Error running pytest:[/red] {e}")
|
|
134
166
|
return False, "", "", fix_attempts, total_cost, model_name
|
|
@@ -139,12 +171,10 @@ def fix_error_loop(unit_test_file: str,
|
|
|
139
171
|
|
|
140
172
|
# Print to console (escaped):
|
|
141
173
|
rprint(f"[magenta]Pytest output:[/magenta]\n{escape_brackets(pytest_output)}")
|
|
142
|
-
|
|
143
|
-
fails, errors, warnings = extract_pytest_summary(pytest_output)
|
|
144
174
|
if verbose:
|
|
145
175
|
rprint(f"[cyan]Iteration summary: {fails} failed, {errors} errors, {warnings} warnings[/cyan]")
|
|
146
176
|
|
|
147
|
-
# If
|
|
177
|
+
# If tests are fully successful, we break out:
|
|
148
178
|
if fails == 0 and errors == 0 and warnings == 0:
|
|
149
179
|
rprint("[green]All tests passed with no warnings! Exiting loop.[/green]")
|
|
150
180
|
break
|
|
@@ -196,7 +226,7 @@ def fix_error_loop(unit_test_file: str,
|
|
|
196
226
|
|
|
197
227
|
# Call fix:
|
|
198
228
|
try:
|
|
199
|
-
#
|
|
229
|
+
# Read error log file into pytest_output so it has history of all previous attempts:
|
|
200
230
|
with open(error_log_file, "r") as f:
|
|
201
231
|
pytest_output = f.read()
|
|
202
232
|
|
|
@@ -222,10 +252,7 @@ def fix_error_loop(unit_test_file: str,
|
|
|
222
252
|
rprint(f"[red]Exceeded the budget of ${budget:.6f}. Ending fixing loop.[/red]")
|
|
223
253
|
break
|
|
224
254
|
|
|
225
|
-
#
|
|
226
|
-
# so skip the old "break if no changes" logic.
|
|
227
|
-
|
|
228
|
-
# If updated_unit_test is True, write to file:
|
|
255
|
+
# Update unit test file if needed.
|
|
229
256
|
if updated_unit_test:
|
|
230
257
|
try:
|
|
231
258
|
with open(unit_test_file, "w") as f:
|
|
@@ -236,7 +263,7 @@ def fix_error_loop(unit_test_file: str,
|
|
|
236
263
|
rprint(f"[red]Error writing updated unit test:[/red] {e}")
|
|
237
264
|
break
|
|
238
265
|
|
|
239
|
-
#
|
|
266
|
+
# Update code file and run verification if needed.
|
|
240
267
|
if updated_code:
|
|
241
268
|
try:
|
|
242
269
|
with open(code_file, "w") as f:
|
|
@@ -274,13 +301,10 @@ def fix_error_loop(unit_test_file: str,
|
|
|
274
301
|
except Exception as e:
|
|
275
302
|
rprint(f"[red]Error restoring backup code file:[/red] {e}")
|
|
276
303
|
break
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
# IMPORTANT: Re-run the tests in the *same* iteration to see if we have fixed the problem:
|
|
280
|
-
# So that if the new code or new test is good, we can break out with exactly one fix_attempt.
|
|
304
|
+
|
|
305
|
+
# Re-run the tests in the same iteration:
|
|
281
306
|
try:
|
|
282
|
-
|
|
283
|
-
second_run_output = second_run_result.stdout + "\n" + second_run_result.stderr
|
|
307
|
+
fails2, errors2, warnings2, second_run_output = run_pytest_on_file(unit_test_file)
|
|
284
308
|
except Exception as e:
|
|
285
309
|
rprint(f"[red]Error running second pytest attempt in iteration {iteration}:[/red] {e}")
|
|
286
310
|
return False, "", "", fix_attempts, total_cost, model_name
|
|
@@ -291,12 +315,10 @@ def fix_error_loop(unit_test_file: str,
|
|
|
291
315
|
|
|
292
316
|
rprint(f"[magenta]Second pytest check:[/magenta]\n{escape_brackets(second_run_output)}")
|
|
293
317
|
|
|
294
|
-
fails2, errors2, warnings2 = extract_pytest_summary(second_run_output)
|
|
295
318
|
if fails2 == 0 and errors2 == 0 and warnings2 == 0:
|
|
296
319
|
rprint("[green]All tests passed on the second run of this iteration! Exiting loop.[/green]")
|
|
297
320
|
break
|
|
298
321
|
else:
|
|
299
|
-
# Update best iteration if needed:
|
|
300
322
|
if (errors2 < best_iteration_info["errors"] or
|
|
301
323
|
(errors2 == best_iteration_info["errors"] and fails2 < best_iteration_info["fails"]) or
|
|
302
324
|
(errors2 == best_iteration_info["errors"] and fails2 == best_iteration_info["fails"] and warnings2 < best_iteration_info["warnings"])):
|
|
@@ -308,16 +330,14 @@ def fix_error_loop(unit_test_file: str,
|
|
|
308
330
|
"unit_test_backup": unit_test_backup,
|
|
309
331
|
"code_backup": code_backup
|
|
310
332
|
}
|
|
311
|
-
# If still not passing, we simply continue to the next iteration in the while loop.
|
|
312
333
|
|
|
313
|
-
#
|
|
334
|
+
# Final test run:
|
|
314
335
|
try:
|
|
315
|
-
|
|
316
|
-
final_result = subprocess.run(final_pytest_cmd, capture_output=True, text=True)
|
|
317
|
-
final_output = final_result.stdout + "\n" + final_result.stderr
|
|
336
|
+
final_fails, final_errors, final_warnings, final_output = run_pytest_on_file(unit_test_file)
|
|
318
337
|
except Exception as e:
|
|
319
338
|
rprint(f"[red]Error running final pytest:[/red] {e}")
|
|
320
339
|
final_output = f"Error: {e}"
|
|
340
|
+
final_fails = final_errors = final_warnings = sys.maxsize
|
|
321
341
|
|
|
322
342
|
with open(error_log_file, "a") as elog:
|
|
323
343
|
elog.write("\n=== Final Pytest Run ===\n")
|
|
@@ -325,13 +345,9 @@ def fix_error_loop(unit_test_file: str,
|
|
|
325
345
|
|
|
326
346
|
rprint(f"[blue]Final pytest output:[/blue]\n{escape_brackets(final_output)}")
|
|
327
347
|
|
|
328
|
-
# Possibly restore best iteration if the final run is not
|
|
329
|
-
# The prompt says: "If the last run isn't the best iteration, restore the best."
|
|
330
|
-
final_fails, final_errors, final_warnings = extract_pytest_summary(final_output)
|
|
348
|
+
# Possibly restore best iteration if the final run is not as good:
|
|
331
349
|
if best_iteration_info["attempt"] is not None:
|
|
332
|
-
# Compare final run to best iteration:
|
|
333
350
|
is_better_final = False
|
|
334
|
-
# If final has strictly fewer errors, or tie then fewer fails, or tie then fewer warnings => keep final
|
|
335
351
|
if final_errors < best_iteration_info["errors"]:
|
|
336
352
|
is_better_final = True
|
|
337
353
|
elif final_errors == best_iteration_info["errors"] and final_fails < best_iteration_info["fails"]:
|
|
@@ -363,8 +379,6 @@ def fix_error_loop(unit_test_file: str,
|
|
|
363
379
|
rprint(f"[red]Error reading final files:[/red] {e}")
|
|
364
380
|
final_unit_test, final_code = "", ""
|
|
365
381
|
|
|
366
|
-
# Check final results for success (no fails, no errors, no warnings)
|
|
367
|
-
final_fails, final_errors, final_warnings = extract_pytest_summary(final_output)
|
|
368
382
|
success = (final_fails == 0 and final_errors == 0 and final_warnings == 0)
|
|
369
383
|
if success:
|
|
370
384
|
rprint("[green]Final tests passed with no warnings.[/green]")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: pdd-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.14
|
|
4
4
|
Summary: PDD (Prompt-Driven Development) Command Line Interface
|
|
5
5
|
Author: Greg Tanaka
|
|
6
6
|
Author-email: glt@alumni.caltech.edu
|
|
@@ -40,7 +40,7 @@ Requires-Dist: semver==3.0.2
|
|
|
40
40
|
Requires-Dist: setuptools==75.1.0
|
|
41
41
|
Requires-Dist: python-Levenshtein
|
|
42
42
|
|
|
43
|
-
.. image:: https://img.shields.io/badge/pdd--cli-v0.0.
|
|
43
|
+
.. image:: https://img.shields.io/badge/pdd--cli-v0.0.14-blue
|
|
44
44
|
:alt: PDD-CLI Version
|
|
45
45
|
|
|
46
46
|
PDD (Prompt-Driven Development) Command Line Interface
|
|
@@ -101,7 +101,7 @@ After installation, verify:
|
|
|
101
101
|
|
|
102
102
|
pdd --version
|
|
103
103
|
|
|
104
|
-
You'll see the current PDD version (e.g., 0.0.
|
|
104
|
+
You'll see the current PDD version (e.g., 0.0.14).
|
|
105
105
|
|
|
106
106
|
Advanced Installation Tips
|
|
107
107
|
--------------------------
|
|
@@ -6,7 +6,7 @@ pdd/bug_main.py,sha256=myKU9--QWdkV4Wf3mD2PoLPJFNgRjwf4z8s7TC28G_s,3720
|
|
|
6
6
|
pdd/bug_to_unit_test.py,sha256=dsJNm6qAwx-m7RvFF5RquFJRzxzZGCWT4IKYnzVCUws,5569
|
|
7
7
|
pdd/change.py,sha256=iqjWS5DrQ73yMkuUQlwIRIFlofmKdaK6t6-v3zHKL-4,4985
|
|
8
8
|
pdd/change_main.py,sha256=yL_i1Ws5vt4vAkWiC826csNi2cHP6wKbwe_PfMqbbPY,11407
|
|
9
|
-
pdd/cli.py,sha256=
|
|
9
|
+
pdd/cli.py,sha256=nFfCGvuBI6CNYj9GYM8xhOOcgkwcQknX1dQt5Cbsc9Y,16593
|
|
10
10
|
pdd/cmd_test_main.py,sha256=aSCxRnSurg15AvPcJDAPp9xy8p_qqnjU1oV14Hi2R54,5301
|
|
11
11
|
pdd/code_generator.py,sha256=n5akrX7VPe71X4RsD6kKqAVvzBLMlciJI4RtJA1PcgA,4375
|
|
12
12
|
pdd/code_generator_main.py,sha256=G2eRBPXc1cGszkk0PbIPmJZHPaf_dw5d2yZbsvQZA3c,4793
|
|
@@ -17,13 +17,13 @@ pdd/construct_paths.py,sha256=8hxkTI_AF5XNpGR4JqCsF4olDBtL8NslXdOZGQt78WM,10039
|
|
|
17
17
|
pdd/context_generator.py,sha256=xLquyM6h40Xqg_wcdowqobrLFyZpIvGrOCJD-OBuoy4,5798
|
|
18
18
|
pdd/context_generator_main.py,sha256=TtsY3jHictdEjmB4cHyNwXmZW_LfHJp3KW3UXyzR2cU,2735
|
|
19
19
|
pdd/continue_generation.py,sha256=hAVySc6oEsM_Zpj5AWBKEZqMWgoLlQBHcFtkAZ9sZ0E,5192
|
|
20
|
-
pdd/crash_main.py,sha256=
|
|
20
|
+
pdd/crash_main.py,sha256=ZvOM-450KiTicSpqLIeJf52x6alx8t0Fq3C11LdQiZU,5464
|
|
21
21
|
pdd/detect_change.py,sha256=ZtgGjGPrD0po-37TEzSbnzFyor7spXHjnT7G6NJ4aCI,5261
|
|
22
22
|
pdd/detect_change_main.py,sha256=1Z4ymhjJaVr2aliGyqkqeqSmQ7QMgcl23p0wdsmBas0,3653
|
|
23
23
|
pdd/find_section.py,sha256=lz_FPY4KDCRAGlL1pWVZiutUNv7E4KsDFK-ymDWA_Ec,962
|
|
24
24
|
pdd/fix_code_loop.py,sha256=L0yxq2yAziPIyFGb8lIP2mvufu8a_gtc5nnN2LuMuKs,8596
|
|
25
25
|
pdd/fix_code_module_errors.py,sha256=M6AnlR2jF5LI-nNg6gIO5LvSkxiaLIUGyTvfnUfe1cU,4625
|
|
26
|
-
pdd/fix_error_loop.py,sha256=
|
|
26
|
+
pdd/fix_error_loop.py,sha256=D6lcWKCYXblpOLASiaj7bWL-Uv6T2u2VyXHObvZuJsw,17520
|
|
27
27
|
pdd/fix_errors_from_unit_tests.py,sha256=8qCEyHZ6lUSBtV9vhQyhgAxDuhngmOy7vVy2HObckd0,8934
|
|
28
28
|
pdd/fix_main.py,sha256=02OIViH12BcsykpDp4Osxw2ndEeThnNakMFkzdpYr48,5333
|
|
29
29
|
pdd/generate_output_paths.py,sha256=zz42GTx9eGyWIYSl3jcWvtJRGnieC3eoPM6DIVcWz2k,7219
|
|
@@ -89,9 +89,9 @@ pdd/prompts/trim_results_start_LLM.prompt,sha256=WwFlOHha4wzMLtRHDMI6GtcNdl2toE8
|
|
|
89
89
|
pdd/prompts/unfinished_prompt_LLM.prompt,sha256=-JgBpiPTQZdWOAwOG1XpfpD9waynFTAT3Jo84eQ4bTw,1543
|
|
90
90
|
pdd/prompts/update_prompt_LLM.prompt,sha256=_lGaxeVP4oF8yGqiN6yj6UE0j79lxfGdjsYr5w5KSYk,1261
|
|
91
91
|
pdd/prompts/xml_convertor_LLM.prompt,sha256=YGRGXJeg6EhM9690f-SKqQrKqSJjLFD51UrPOlO0Frg,2786
|
|
92
|
-
pdd_cli-0.0.
|
|
93
|
-
pdd_cli-0.0.
|
|
94
|
-
pdd_cli-0.0.
|
|
95
|
-
pdd_cli-0.0.
|
|
96
|
-
pdd_cli-0.0.
|
|
97
|
-
pdd_cli-0.0.
|
|
92
|
+
pdd_cli-0.0.14.dist-info/LICENSE,sha256=-1bjYH-CEjGEQ8VixtnRYuu37kN6F9NxmZSDkBuUQ9o,1062
|
|
93
|
+
pdd_cli-0.0.14.dist-info/METADATA,sha256=GhFCBBWPSLEgXkVJokItqOxM6tZFMYmXTTJNvKAronU,6808
|
|
94
|
+
pdd_cli-0.0.14.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
95
|
+
pdd_cli-0.0.14.dist-info/entry_points.txt,sha256=Kr8HtNVb8uHZtQJNH4DnF8j7WNgWQbb7_Pw5hECSR-I,36
|
|
96
|
+
pdd_cli-0.0.14.dist-info/top_level.txt,sha256=xjnhIACeMcMeDfVNREgQZl4EbTni2T11QkL5r7E-sbE,4
|
|
97
|
+
pdd_cli-0.0.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|