Coverage for src/irorun/helpers.py: 100%

65 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-03-02 16:47 -0500

1# Copyright 2025 Faith O. Oyedemi 

2# SPDX-License-Identifier: Apache-2.0 

3# 

4# Licensed under the Apache License, Version 2.0 (the "License"); 

5# you may not use this file except in compliance with the License. 

6# You may obtain a copy of the License at 

7# 

8# http://www.apache.org/licenses/LICENSE-2.0 

9# 

10# Unless required by applicable law or agreed to in writing, software 

11# distributed under the License is distributed on an "AS IS" BASIS, 

12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 

13# See the License for the specific language governing permissions and 

14# limitations under the License. 

15# 

16# For more details, see the full text of the Apache License at: 

17# http://www.apache.org/licenses/LICENSE-2.0 

18 

19import os 

20import subprocess 

21from collections.abc import Iterable 

22from contextlib import contextmanager 

23from enum import Enum 

24from pathlib import Path 

25from typing import Optional 

26 

27import typer 

28 

29 

30class EnvManager(Enum): 

31 POETRY = 'poetry' 

32 UV = 'uv' 

33 VIRTUALENV = 'virtualenv' 

34 

35 

36@contextmanager 

37def change_dir(new_dir: str): 

38 """Context manager for changing the current working directory.""" 

39 previous_dir = os.getcwd() 

40 os.chdir(new_dir) 

41 try: 

42 yield 

43 finally: 

44 os.chdir(previous_dir) 

45 

46 

47def run_command(cmd: list[str], cwd: Optional[str] = None) -> None: 

48 """Wrapper for subprocess.run to execute a command and handle errors.""" 

49 try: 

50 subprocess.run(cmd, check=True, cwd=cwd) 

51 except subprocess.CalledProcessError as e: 

52 typer.echo(f'Error running command {cmd}: {e}', err=True) 

53 raise typer.Exit(1) from e 

54 

55 

56def add_dependencies(package_manager: EnvManager, dependencies: list[str]) -> None: 

57 """ 

58 Adds dependencies using the specified package manager. 

59 Only supports POETRY and UV. 

60 """ 

61 if package_manager not in (EnvManager.POETRY, EnvManager.UV): 

62 typer.echo('Invalid package manager', err=True) 

63 raise typer.Exit(1) 

64 run_command([package_manager.value, 'add'] + dependencies) 

65 typer.echo(f'Added dependencies: {dependencies}') 

66 

67 

68def create_poetry_project( 

69 project_dir: str, dependencies: Optional[list[str]] = None 

70) -> None: 

71 """ 

72 Creates a new Poetry project and optionally installs dependencies. 

73 """ 

74 run_command(['poetry', 'new', project_dir]) 

75 typer.echo(f'Created new Poetry project in {project_dir}') 

76 with change_dir(project_dir): 

77 if dependencies: 

78 typer.echo(f'Installing dependencies: {dependencies}') 

79 add_dependencies(EnvManager.POETRY, dependencies) 

80 

81 

82def create_uv_project( 

83 project_dir: str, venv_name: str, dependencies: Optional[list[str]] = None 

84) -> None: 

85 """ 

86 Creates a new project with a virtual environment using uv. 

87 """ 

88 run_command(['uv', 'init', project_dir]) 

89 with change_dir(project_dir): 

90 run_command(['uv', 'venv', venv_name]) 

91 typer.echo(f'Created virtual environment "{venv_name}" in {project_dir}') 

92 if dependencies: 

93 typer.echo(f'Installing dependencies: {dependencies}') 

94 add_dependencies(EnvManager.UV, dependencies) 

95 

96 

97def create_virtualenv_project( 

98 project_dir: str, venv_name: str, dependencies: Optional[list[str]] = None 

99) -> None: 

100 """ 

101 Creates a new project directory and virtual environment using virtualenv. 

102 """ 

103 Path(project_dir).mkdir(parents=True, exist_ok=True) 

104 with change_dir(project_dir): 

105 run_command(['virtualenv', venv_name]) 

106 

107 # Determine the correct path for the pip executable in the virtual environment. 

108 venv_path = Path(venv_name) 

109 bin_dir = 'Scripts' if os.name == 'nt' else 'bin' 

110 pip_executable = str(venv_path / bin_dir / 'pip') 

111 

112 run_command([pip_executable, 'install', '-U', 'pip']) 

113 if dependencies: 

114 typer.echo(f'Installing dependencies: {dependencies}') 

115 run_command([pip_executable, 'install'] + dependencies) 

116 typer.echo('Dependencies installed successfully.') 

117 typer.echo(f'Created virtual environment project in {project_dir}') 

118 

119 

120def create_subdirectories(project_dir: str, subdirectories: Iterable[str]) -> None: 

121 """ 

122 Creates subdirectories within a project directory. 

123 

124 Parameters: 

125 project_dir: The base project directory. 

126 subdirectories: An iterable of subdirectory names to create under project_dir. 

127 """ 

128 base = Path(project_dir) 

129 for subdir in subdirectories: 

130 sub_path = base / subdir 

131 sub_path.mkdir(parents=True, exist_ok=True) 

132 typer.echo(f'Created subdirectory: {sub_path}')