pdd-cli 0.0.40__py3-none-any.whl → 0.0.41__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/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """PDD - Prompt Driven Development"""
2
2
 
3
- __version__ = "0.0.40"
3
+ __version__ = "0.0.41"
4
4
 
5
5
  # Strength parameter used for LLM extraction across the codebase
6
6
  # Used in postprocessing, XML tagging, code generation, and other extraction
pdd/auto_update.py CHANGED
@@ -1,10 +1,12 @@
1
1
  """This module provides a function to automatically update the package."""
2
2
  import importlib.metadata
3
- import requests
4
- import semver
3
+ import shutil
5
4
  import subprocess
6
5
  import sys
7
- import shutil
6
+ from typing import Optional
7
+ import requests
8
+ import semver
9
+
8
10
 
9
11
  def detect_installation_method(sys_executable):
10
12
  """
@@ -39,12 +41,59 @@ def get_upgrade_command(package_name, installation_method):
39
41
  uv_path = shutil.which("uv")
40
42
  if uv_path:
41
43
  return ([uv_path, "tool", "install", package_name, "--force"], False)
42
- else:
43
- # If uv isn't in PATH, use shell=True
44
- return (["uv", "tool", "install", package_name, "--force"], True)
45
- else:
46
- # Default pip method
47
- return ([sys.executable, "-m", "pip", "install", "--upgrade", package_name], False)
44
+ # If uv isn't in PATH, use shell=True
45
+ return (["uv", "tool", "install", package_name, "--force"], True)
46
+ # Default pip method
47
+ return ([sys.executable, "-m", "pip", "install", "--upgrade", package_name], False)
48
+
49
+
50
+ def _get_latest_version(package_name: str) -> Optional[str]:
51
+ """Fetch the latest version of a package from PyPI."""
52
+ # pylint: disable=broad-except
53
+ try:
54
+ pypi_url = f"https://pypi.org/pypi/{package_name}/json"
55
+ response = requests.get(pypi_url, timeout=10)
56
+ response.raise_for_status()
57
+ return response.json()['info']['version']
58
+ except Exception as ex:
59
+ print(f"Failed to fetch latest version from PyPI: {str(ex)}")
60
+ return None
61
+
62
+
63
+ def _upgrade_package(package_name: str, installation_method: str):
64
+ """Upgrade a package using the specified installation method."""
65
+ cmd, use_shell = get_upgrade_command(package_name, installation_method)
66
+ cmd_str = " ".join(cmd)
67
+ print(f"\nDetected installation method: {installation_method}")
68
+ print(f"Upgrading with command: {cmd_str}")
69
+
70
+ # pylint: disable=broad-except
71
+ try:
72
+ result = subprocess.run(
73
+ cmd,
74
+ shell=use_shell,
75
+ capture_output=True,
76
+ text=True,
77
+ check=False
78
+ )
79
+ if result.returncode == 0:
80
+ print(f"\nSuccessfully upgraded {package_name}")
81
+ return True
82
+ print(f"\nUpgrade command failed: {result.stderr}")
83
+ return False
84
+ except Exception as ex:
85
+ print(f"\nError during upgrade: {str(ex)}")
86
+ return False
87
+
88
+
89
+ def _is_new_version_available(current_version: str, latest_version: str) -> bool:
90
+ """Check if a new version is available."""
91
+ try:
92
+ current_semver = semver.VersionInfo.parse(current_version)
93
+ latest_semver = semver.VersionInfo.parse(latest_version)
94
+ return latest_semver > current_semver
95
+ except ValueError:
96
+ return latest_version != current_version
48
97
 
49
98
 
50
99
  def auto_update(package_name: str = "pdd-cli", latest_version: str = None) -> None:
@@ -56,92 +105,38 @@ def auto_update(package_name: str = "pdd-cli", latest_version: str = None) -> No
56
105
  latest_version (str): Known latest version (default: None)
57
106
  package_name (str): Name of the package to check (default: "pdd-cli")
58
107
  """
108
+ # pylint: disable=broad-except
59
109
  try:
60
- # Get current installed version
61
110
  current_version = importlib.metadata.version(package_name)
62
111
 
63
- # If latest_version is not provided, fetch from PyPI
64
112
  if latest_version is None:
65
- try:
66
- pypi_url = f"https://pypi.org/pypi/{package_name}/json"
67
- response = requests.get(pypi_url, timeout=10)
68
- response.raise_for_status()
69
- latest_version = response.json()['info']['version']
70
- except Exception as ex:
71
- print(f"Failed to fetch latest version from PyPI: {str(ex)}")
113
+ latest_version = _get_latest_version(package_name)
114
+ if latest_version is None:
72
115
  return
73
116
 
74
- # Compare versions using semantic versioning
75
- try:
76
- current_semver = semver.VersionInfo.parse(current_version)
77
- latest_semver = semver.VersionInfo.parse(latest_version)
78
- except ValueError:
79
- # If versions don't follow semantic versioning, fall back to string comparison
80
- if current_version == latest_version:
81
- return
82
- else:
83
- # If versions follow semantic versioning, compare properly
84
- if current_semver >= latest_semver:
85
- return
117
+ if not _is_new_version_available(current_version, latest_version):
118
+ return
86
119
 
87
- # If we get here, there's a new version available
88
- print(f"\nNew version of {package_name} available: {latest_version} (current: {current_version})")
120
+ print(f"\nNew version of {package_name} available: "
121
+ f"{latest_version} (current: {current_version})")
89
122
 
90
- # Ask for user confirmation
91
123
  while True:
92
124
  response = input("Would you like to upgrade? [y/N]: ").lower().strip()
93
125
  if response in ['y', 'yes']:
94
- # Detect installation method
95
126
  installation_method = detect_installation_method(sys.executable)
96
- cmd, use_shell = get_upgrade_command(package_name, installation_method)
127
+ if _upgrade_package(package_name, installation_method):
128
+ break
97
129
 
98
- cmd_str = " ".join(cmd)
99
- print(f"\nDetected installation method: {installation_method}")
100
- print(f"Upgrading with command: {cmd_str}")
130
+ if installation_method == "uv":
131
+ print("\nAttempting fallback to pip...")
132
+ if _upgrade_package(package_name, "pip"):
133
+ break
101
134
 
102
- try:
103
- result = subprocess.run(
104
- cmd,
105
- shell=use_shell,
106
- capture_output=True,
107
- text=True,
108
- check=False
109
- )
110
-
111
- if result.returncode == 0:
112
- print(f"\nSuccessfully upgraded {package_name} to version {latest_version}")
113
- else:
114
- print(f"\nUpgrade command failed: {result.stderr}")
115
-
116
- # If UV failed and we're not already in fallback mode, try pip as fallback
117
- if installation_method == "uv":
118
- print("\nAttempting fallback to pip...")
119
- fallback_cmd, fallback_shell = get_upgrade_command(package_name, "pip")
120
- fallback_str = " ".join(fallback_cmd)
121
- print(f"Fallback command: {fallback_str}")
122
-
123
- try:
124
- fallback_result = subprocess.run(
125
- fallback_cmd,
126
- shell=fallback_shell,
127
- capture_output=True,
128
- text=True,
129
- check=False
130
- )
131
- if fallback_result.returncode == 0:
132
- print(f"\nSuccessfully upgraded {package_name} using fallback method")
133
- else:
134
- print(f"\nFallback upgrade failed: {fallback_result.stderr}")
135
- except Exception as fallback_ex:
136
- print(f"\nError during fallback upgrade: {str(fallback_ex)}")
137
- except Exception as ex:
138
- print(f"\nError during upgrade: {str(ex)}")
139
135
  break
140
- elif response in ['n', 'no', '']:
136
+ if response in ['n', 'no', '']:
141
137
  print("\nUpgrade cancelled")
142
138
  break
143
- else:
144
- print("Please answer 'y' or 'n'")
139
+ print("Please answer 'y' or 'n'")
145
140
 
146
141
  except importlib.metadata.PackageNotFoundError:
147
142
  print(f"Package {package_name} is not installed")
pdd/bug_main.py CHANGED
@@ -76,8 +76,8 @@ def bug_main(
76
76
  program_content,
77
77
  strength,
78
78
  temperature,
79
- language,
80
- time_budget
79
+ time_budget,
80
+ language
81
81
  )
82
82
 
83
83
  # Save results if output path is provided
pdd/bug_to_unit_test.py CHANGED
@@ -1,5 +1,7 @@
1
- from typing import Tuple, Optional
2
- from rich import print
1
+ """
2
+ This module provides functionality to generate a unit test based on a bug report.
3
+ """
4
+ from typing import Tuple
3
5
  from rich.markdown import Markdown
4
6
  from rich.console import Console
5
7
  from . import EXTRACTION_STRENGTH, DEFAULT_STRENGTH, DEFAULT_TIME
@@ -12,7 +14,8 @@ from .preprocess import preprocess
12
14
 
13
15
  console = Console()
14
16
 
15
- def bug_to_unit_test(
17
+
18
+ def bug_to_unit_test( # pylint: disable=too-many-arguments, too-many-locals
16
19
  current_output: str,
17
20
  desired_output: str,
18
21
  prompt_used_to_generate_the_code: str,
@@ -21,7 +24,7 @@ def bug_to_unit_test(
21
24
  strength: float = DEFAULT_STRENGTH,
22
25
  temperature: float = 0.0,
23
26
  time: float = DEFAULT_TIME,
24
- language: str = "python"
27
+ language: str = "python",
25
28
  ) -> Tuple[str, float, str]:
26
29
  """
27
30
  Generate a unit test from a code file with bug information.
@@ -32,7 +35,8 @@ def bug_to_unit_test(
32
35
  prompt_used_to_generate_the_code (str): Original prompt used to generate the code
33
36
  code_under_test (str): Code to be tested
34
37
  program_used_to_run_code_under_test (str): Program used to run the code
35
- strength (float, optional): Strength of the LLM model. Must be between 0 and 1. Defaults to DEFAULT_STRENGTH.
38
+ strength (float, optional): Strength of the LLM model. Must be between 0 and 1.
39
+ Defaults to DEFAULT_STRENGTH.
36
40
  temperature (float, optional): Temperature of the LLM model. Defaults to 0.0.
37
41
  time (float, optional): Time budget for LLM calls. Defaults to DEFAULT_TIME.
38
42
  language (str, optional): Programming language. Defaults to "python".
@@ -46,11 +50,14 @@ def bug_to_unit_test(
46
50
  # Validate strength parameter
47
51
  if not 0 <= strength <= 1:
48
52
  raise ValueError("Strength parameter must be between 0 and 1")
49
-
50
- # Ensure language parameter is not None or empty
53
+
54
+ # Ensure language parameter is not None or empty, defaulting to "python" if it is.
55
+ # This single check is sufficient for the whole function.
51
56
  if not language or not isinstance(language, str):
52
- language = "python" # Default fallback
53
- console.print("[yellow]Warning: Invalid language parameter, defaulting to 'python'[/yellow]")
57
+ language = "python"
58
+ console.print(
59
+ "[yellow]Warning: Invalid or missing language parameter, defaulting to 'python'[/yellow]"
60
+ )
54
61
 
55
62
  total_cost = 0.0
56
63
  final_model_name = ""
@@ -62,15 +69,15 @@ def bug_to_unit_test(
62
69
  raise ValueError("Failed to load prompt template")
63
70
 
64
71
  # Step 2: Prepare input and run through LLM
65
- preprocessed_prompt = preprocess(prompt_used_to_generate_the_code)
66
-
72
+ preprocessed_prompt = preprocess(prompt_used_to_generate_the_code, double_curly_brackets=False)
73
+
67
74
  input_json = {
68
75
  "prompt_that_generated_code": preprocessed_prompt,
69
76
  "current_output": current_output,
70
77
  "desired_output": desired_output,
71
78
  "code_under_test": code_under_test,
72
79
  "program_used_to_run_code_under_test": program_used_to_run_code_under_test,
73
- "language": language if language and isinstance(language, str) else "python"
80
+ "language": language, # Simplified: language is guaranteed to be a valid string
74
81
  }
75
82
 
76
83
  console.print("[bold blue]Generating unit test...[/bold blue]")
@@ -80,57 +87,56 @@ def bug_to_unit_test(
80
87
  strength=strength,
81
88
  temperature=temperature,
82
89
  time=time,
83
- verbose=True
90
+ verbose=True,
84
91
  )
85
92
 
86
- total_cost += response['cost']
87
- final_model_name = response['model_name']
93
+ total_cost += response["cost"]
94
+ final_model_name = response["model_name"]
88
95
 
89
96
  # Step 3: Print markdown formatting
90
- console.print(Markdown(response['result']))
97
+ console.print(Markdown(response["result"]))
91
98
 
92
99
  # Step 4: Check if generation is complete
93
- last_600_chars = response['result'][-600:] if len(response['result']) > 600 else response['result']
94
-
95
- reasoning, is_finished, unfinished_cost, unfinished_model = unfinished_prompt(
100
+ last_600_chars = (
101
+ response["result"][-600:]
102
+ if len(response["result"]) > 600
103
+ else response["result"]
104
+ )
105
+
106
+ _reasoning, is_finished, unfinished_cost, _unfinished_model = unfinished_prompt(
96
107
  prompt_text=last_600_chars,
97
- strength=0.75,
108
+ strength=0.89,
98
109
  temperature=temperature,
99
110
  time=time,
100
- verbose=False
111
+ verbose=False,
101
112
  )
102
-
113
+
103
114
  total_cost += unfinished_cost
104
115
 
105
116
  if not is_finished:
106
117
  console.print("[yellow]Generation incomplete. Continuing...[/yellow]")
107
118
  continued_output, continued_cost, continued_model = continue_generation(
108
119
  formatted_input_prompt=prompt_template,
109
- llm_output=response['result'],
120
+ llm_output=response["result"],
110
121
  strength=strength,
111
122
  temperature=temperature,
112
123
  time=time,
113
- verbose=True
124
+ verbose=True,
114
125
  )
115
126
  total_cost += continued_cost
116
127
  final_model_name = continued_model
117
128
  result = continued_output
118
129
  else:
119
- result = response['result']
130
+ result = response["result"]
120
131
 
121
132
  # Post-process the result
122
- # Double-check language is valid before passing to postprocess
123
- if not language or not isinstance(language, str):
124
- language = "python" # Ensure language is valid
125
- console.print("[yellow]Warning: Language value became invalid during processing, defaulting to 'python'[/yellow]")
126
-
127
- final_code, postprocess_cost, postprocess_model = postprocess(
133
+ final_code, postprocess_cost, _postprocess_model = postprocess(
128
134
  result,
129
135
  language,
130
136
  strength=EXTRACTION_STRENGTH,
131
137
  temperature=temperature,
132
138
  time=time,
133
- verbose=True
139
+ verbose=True,
134
140
  )
135
141
  total_cost += postprocess_cost
136
142
 
@@ -139,10 +145,11 @@ def bug_to_unit_test(
139
145
 
140
146
  return final_code, total_cost, final_model_name
141
147
 
142
- except Exception as e:
143
- console.print(f"[bold red]Error: {str(e)}[/bold red]")
148
+ except Exception as ex: # pylint: disable=broad-except
149
+ console.print(f"[bold red]Error: {str(ex)}[/bold red]")
144
150
  return "", 0.0, ""
145
151
 
152
+
146
153
  def main():
147
154
  """Example usage of the bug_to_unit_test function"""
148
155
  try:
@@ -161,7 +168,7 @@ def add_numbers(a, b):
161
168
  prompt_used_to_generate_the_code=prompt,
162
169
  code_under_test=code,
163
170
  program_used_to_run_code_under_test=program,
164
- time=DEFAULT_TIME
171
+ time=DEFAULT_TIME,
165
172
  )
166
173
 
167
174
  if unit_test:
@@ -170,8 +177,9 @@ def add_numbers(a, b):
170
177
  console.print(f"[bold blue]Total Cost: ${cost:.6f}[/bold blue]")
171
178
  console.print(f"[bold blue]Model Used: {model}[/bold blue]")
172
179
 
173
- except Exception as e:
174
- console.print(f"[bold red]Error in main: {str(e)}[/bold red]")
180
+ except Exception as ex: # pylint: disable=broad-except
181
+ console.print(f"[bold red]Error in main: {str(ex)}[/bold red]")
182
+
175
183
 
176
184
  if __name__ == "__main__":
177
- main()
185
+ main()
pdd/change.py CHANGED
@@ -1,3 +1,7 @@
1
+ """
2
+ This module provides functionality to modify a prompt according to specified changes.
3
+ It takes an input prompt, input code, and change instructions to generate a modified prompt.
4
+ """
1
5
  from typing import Tuple
2
6
  from rich.console import Console
3
7
  from rich.markdown import Markdown
@@ -11,16 +15,17 @@ from . import EXTRACTION_STRENGTH, DEFAULT_STRENGTH, DEFAULT_TIME
11
15
  console = Console()
12
16
 
13
17
  class ExtractedPrompt(BaseModel):
18
+ """Pydantic model for extracting the modified prompt from LLM output."""
14
19
  modified_prompt: str = Field(description="The extracted modified prompt")
15
20
 
16
- def change(
21
+ def change( # pylint: disable=too-many-arguments, too-many-locals
17
22
  input_prompt: str,
18
23
  input_code: str,
19
24
  change_prompt: str,
20
25
  strength: float = DEFAULT_STRENGTH,
21
26
  temperature: float = 0.0,
22
27
  time: float = DEFAULT_TIME,
23
- budget: float = 5.0, # Note: budget is in the spec but not used. Keeping for now.
28
+ budget: float = 5.0, # pylint: disable=unused-argument
24
29
  verbose: bool = False
25
30
  ) -> Tuple[str, float, str]:
26
31
  """
@@ -33,7 +38,7 @@ def change(
33
38
  strength (float): The strength parameter for the LLM model (0-1)
34
39
  temperature (float): The temperature parameter for the LLM model
35
40
  time (float): The time budget for LLM calls.
36
- budget (float): The budget for the operation (currently unused directly by llm_invoke).
41
+ budget (float): The budget for the operation (not used, but kept for API compatibility).
37
42
  verbose (bool): Whether to print out detailed information.
38
43
 
39
44
  Returns:
@@ -48,13 +53,15 @@ def change(
48
53
  raise ValueError("Failed to load prompt templates")
49
54
 
50
55
  # Step 2: Preprocess the change_LLM prompt
51
- processed_change_llm_template = preprocess(change_llm_prompt_template, recursive=False, double_curly_brackets=False)
52
- processed_change_prompt_content = preprocess(change_prompt, recursive=False, double_curly_brackets=False)
56
+ processed_change_llm_template = preprocess(change_llm_prompt_template,
57
+ recursive=False, double_curly_brackets=False)
58
+ processed_change_prompt_content = preprocess(change_prompt,
59
+ recursive=False, double_curly_brackets=False)
53
60
 
54
61
  # Input validation
55
62
  if not all([input_prompt, input_code, processed_change_prompt_content]):
56
63
  raise ValueError("Missing required input parameters after preprocessing")
57
- if not (0 <= strength <= 1):
64
+ if not 0 <= strength <= 1:
58
65
  raise ValueError("Strength must be between 0 and 1")
59
66
 
60
67
  total_cost = 0.0
@@ -115,10 +122,10 @@ def change(
115
122
  # Step 7: Return results
116
123
  return modified_prompt, total_cost, final_model_name
117
124
 
118
- except Exception as e:
125
+ except Exception as error:
119
126
  # Conditionally print error if verbose
120
- if verbose:
121
- console.print(f"[red]Error in change function: {str(e)}[/red]")
127
+ if verbose:
128
+ console.print(f"[red]Error in change function: {str(error)}[/red]")
122
129
  raise
123
130
 
124
131
  def main():
@@ -128,7 +135,7 @@ def main():
128
135
  input_prompt_content = "Write a function that adds two numbers"
129
136
  input_code_content = "def add(a, b):\n return a + b"
130
137
  change_prompt_content = "Make the function handle negative numbers explicitly"
131
-
138
+
132
139
  modified_prompt, cost, model = change(
133
140
  input_prompt=input_prompt_content,
134
141
  input_code=input_code_content,
@@ -145,8 +152,8 @@ def main():
145
152
  console.print(f"Total Cost: ${cost:.6f}")
146
153
  console.print(f"Model Used: {model}")
147
154
 
148
- except Exception as e:
149
- console.print(f"[red]Error in main: {str(e)}[/red]")
155
+ except Exception as error: # pylint: disable=broad-except
156
+ console.print(f"[red]Error in main: {str(error)}[/red]")
150
157
 
151
158
  if __name__ == "__main__":
152
- main()
159
+ main()