wrd 0.1.2__py2.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.
wrd/__version__.py ADDED
@@ -0,0 +1,7 @@
1
+ """Version information for WRD package."""
2
+
3
+ # This version should match the version in pyproject.toml
4
+ __version__ = "0.1.2"
5
+ __author__ = "Tom Sapletta"
6
+ __license__ = "Apache-2.0"
7
+ __email__ = "info@softreck.dev"
wrd/cli.py ADDED
@@ -0,0 +1,312 @@
1
+ """WRD Command Line Interface."""
2
+
3
+ import os
4
+ import sys
5
+ import yaml
6
+ from pathlib import Path
7
+ from typing import Dict, Any, Optional, List
8
+ from rich.console import Console
9
+ from rich.prompt import Prompt, Confirm, IntPrompt
10
+ from rich.panel import Panel
11
+ from rich.table import Table
12
+ from rich.progress import track
13
+ import subprocess
14
+ import shutil
15
+
16
+ console = Console()
17
+
18
+ class WRDConfig:
19
+ """Manage WRD configuration."""
20
+
21
+ def __init__(self):
22
+ self.config_dir = Path.home() / ".wrd"
23
+ self.config_file = self.config_dir / "config.yaml"
24
+ self.templates_dir = self.config_dir / "templates"
25
+ self.ensure_directories()
26
+ self.config = self.load_config()
27
+
28
+ def ensure_directories(self):
29
+ """Ensure configuration directories exist."""
30
+ self.config_dir.mkdir(exist_ok=True)
31
+ self.templates_dir.mkdir(exist_ok=True)
32
+
33
+ def load_config(self) -> Dict[str, Any]:
34
+ """Load configuration from YAML file."""
35
+ if not self.config_file.exists():
36
+ return {
37
+ "projects_dir": str(Path.home() / "projects"),
38
+ "editor": os.environ.get("EDITOR", "code"),
39
+ "default_language": "python",
40
+ "templates": {}
41
+ }
42
+
43
+ with open(self.config_file, 'r') as f:
44
+ return yaml.safe_load(f) or {}
45
+
46
+ def save_config(self):
47
+ """Save configuration to YAML file."""
48
+ with open(self.config_file, 'w') as f:
49
+ yaml.dump(self.config, f, default_flow_style=False)
50
+
51
+ class WRDShell:
52
+ """Interactive WRD shell."""
53
+
54
+ def __init__(self):
55
+ self.config = WRDConfig()
56
+ self.current_project = None
57
+ self.console = Console()
58
+
59
+ def print_banner(self):
60
+ """Print WRD banner."""
61
+ banner = """
62
+ ██╗ ██╗██████╗ ██████╗
63
+ ██║ ██║██╔══██╗██╔══██╗
64
+ ██║ █╗ ██║██████╔╝██║ ██║
65
+ ██║███╗██║██╔══██╗██║ ██║
66
+ ╚███╔███╔╝██║ ██║██████╔╝
67
+ ╚══╝╚══╝ ╚═╝ ╚═╝╚═════╝
68
+ WRD - WRonai Development Tool
69
+ """
70
+ self.console.print(Panel.fit(banner, style="bold blue"))
71
+
72
+ def get_menu_choice(self, title: str, options: List[Dict[str, str]], timeout: int = 10) -> str:
73
+ """Display a menu and get user choice."""
74
+ table = Table(title=title, show_header=False, show_lines=True)
75
+ table.add_column("Option", style="cyan")
76
+ table.add_column("Description")
77
+
78
+ for i, option in enumerate(options, 1):
79
+ table.add_row(str(i), option['description'])
80
+
81
+ self.console.print(table)
82
+
83
+ try:
84
+ choice = IntPrompt.ask(
85
+ "\nEnter your choice",
86
+ choices=[str(i) for i in range(1, len(options) + 1)],
87
+ timeout=timeout
88
+ )
89
+ return options[int(choice) - 1]['command']
90
+ except Exception:
91
+ self.console.print("\n[bold red]Timeout reached. Using default option.[/]")
92
+ return options[0]['command']
93
+
94
+ def init_project(self, project_path: Optional[str] = None):
95
+ """Initialize a new project."""
96
+ if project_path is None:
97
+ project_path = Prompt.ask("Enter project path", default=str(Path.cwd()))
98
+
99
+ project_path = Path(project_path).absolute()
100
+
101
+ if project_path.exists() and any(project_path.iterdir()):
102
+ self.console.print(f"[yellow]Directory {project_path} is not empty.[/]")
103
+ if not Confirm.ask("Do you want to initialize a project here anyway?"):
104
+ return
105
+
106
+ # Get project details
107
+ project_name = project_path.name
108
+ language = Prompt.ask("Project language", default=self.config.config.get('default_language', 'python'))
109
+ description = Prompt.ask("Project description", default="")
110
+
111
+ # Create project structure
112
+ project_path.mkdir(parents=True, exist_ok=True)
113
+
114
+ # Initialize project based on language
115
+ if language == 'python':
116
+ self._init_python_project(project_path, project_name, description)
117
+ # Add more language support here
118
+
119
+ self.console.print(f"[green]✓ Project {project_name} initialized successfully![/]")
120
+
121
+ def _init_python_project(self, project_path: Path, name: str, description: str):
122
+ """Initialize a Python project."""
123
+ # Create basic Python project structure
124
+ (project_path / 'src' / name.replace('-', '_')).mkdir(parents=True, exist_ok=True)
125
+ (project_path / 'tests').mkdir(exist_ok=True)
126
+
127
+ # Create pyproject.toml
128
+ pyproject = f"""[build-system]
129
+ requires = ["setuptools>=42.0"]
130
+ build-backend = "setuptools.build_meta"
131
+
132
+ [project]
133
+ name = "{name}"
134
+ version = "0.1.0"
135
+ description = "{description}"
136
+ authors = [
137
+ {{ name = "Your Name", email = "your.email@example.com" }}
138
+ ]
139
+ readme = "README.md"
140
+ requires-python = ">=3.8"
141
+
142
+ [project.optional-dependencies]
143
+ dev = [
144
+ "pytest>=6.0",
145
+ "black>=21.12b0",
146
+ "isort>=5.10.1",
147
+ "mypy>=0.910",
148
+ "pytest-cov>=2.0"
149
+ ]
150
+ """
151
+ (project_path / 'pyproject.toml').write_text(pyproject)
152
+
153
+ # Create README.md
154
+ readme = f"""# {name}
155
+
156
+ {description}
157
+
158
+ ## Installation
159
+
160
+ ```bash
161
+ pip install -e .
162
+ ```
163
+
164
+ ## Development
165
+
166
+ Install development dependencies:
167
+
168
+ ```bash
169
+ pip install -e ".[dev]"
170
+ ```
171
+
172
+ Run tests:
173
+
174
+ ```bash
175
+ pytest
176
+ ```
177
+ """
178
+ (project_path / 'README.md').write_text(readme)
179
+
180
+ # Create .gitignore
181
+ gitignore = """# Python
182
+ __pycache__/
183
+ *.py[cod]
184
+ *$py.class
185
+ *.so
186
+ .Python
187
+ build/
188
+ develop-eggs/
189
+ dist/
190
+ downloads/
191
+ eggs/
192
+ .eggs/
193
+ lib/
194
+ lib64/
195
+ parts/
196
+ sdist/
197
+ var/
198
+ wheels/
199
+ *.egg-info/
200
+ .installed.cfg
201
+ *.egg
202
+
203
+ # Virtual Environment
204
+ venv/
205
+ env/
206
+
207
+ # IDE
208
+ .idea/
209
+ .vscode/
210
+ *.swp
211
+ *.swo
212
+
213
+ # Testing
214
+ .coverage
215
+ htmlcov/
216
+ .pytest_cache/
217
+ """
218
+ (project_path / '.gitignore').write_text(gitignore)
219
+
220
+ # Initialize git repository
221
+ try:
222
+ subprocess.run(['git', 'init'], cwd=project_path, check=True)
223
+ subprocess.run(['git', 'add', '.'], cwd=project_path, check=True)
224
+ subprocess.run(['git', 'commit', '-m', 'Initial commit'], cwd=project_path, check=True)
225
+ except Exception as e:
226
+ self.console.print(f"[yellow]Warning: Failed to initialize git repository: {e}[/]")
227
+
228
+ def shell(self):
229
+ """Start interactive shell."""
230
+ self.print_banner()
231
+
232
+ while True:
233
+ try:
234
+ options = [
235
+ {'command': 'init', 'description': 'Initialize a new project'},
236
+ {'command': 'list', 'description': 'List all projects'},
237
+ {'command': 'config', 'description': 'Configure WRD'},
238
+ {'command': 'exit', 'description': 'Exit WRD shell'}
239
+ ]
240
+
241
+ choice = self.get_menu_choice("WRD - Main Menu", options, timeout=60)
242
+
243
+ if choice == 'init':
244
+ self.init_project()
245
+ elif choice == 'list':
246
+ self.list_projects()
247
+ elif choice == 'config':
248
+ self.configure()
249
+ elif choice == 'exit':
250
+ self.console.print("[green]Goodbye![/]")
251
+ break
252
+
253
+ except KeyboardInterrupt:
254
+ self.console.print("\n[red]Operation cancelled.[/]")
255
+ except Exception as e:
256
+ self.console.print(f"[red]Error: {e}[/]")
257
+
258
+ def list_projects(self):
259
+ """List all projects in the projects directory."""
260
+ projects_dir = Path(self.config.config.get('projects_dir', Path.home() / 'projects'))
261
+
262
+ if not projects_dir.exists():
263
+ self.console.print("[yellow]No projects directory found.[/]")
264
+ return
265
+
266
+ table = Table(title="Projects")
267
+ table.add_column("Name", style="cyan")
268
+ table.add_column("Path")
269
+ table.add_column("Modified")
270
+
271
+ for project_dir in projects_dir.iterdir():
272
+ if project_dir.is_dir():
273
+ modified = datetime.fromtimestamp(project_dir.stat().st_mtime).strftime('%Y-%m-%d %H:%M')
274
+ table.add_row(project_dir.name, str(project_dir), modified)
275
+
276
+ self.console.print(table)
277
+
278
+ def configure(self):
279
+ """Configure WRD settings."""
280
+ self.console.print("[bold]Current Configuration:[/]")
281
+ for key, value in self.config.config.items():
282
+ self.console.print(f" [cyan]{key}:[/] {value}")
283
+
284
+ if Confirm.ask("\nDo you want to change any settings?"):
285
+ setting = Prompt.ask("Enter setting name")
286
+ if setting in self.config.config:
287
+ if isinstance(self.config.config[setting], bool):
288
+ new_value = Confirm.ask(f"New value for {setting}")
289
+ else:
290
+ new_value = Prompt.ask(f"New value for {setting}")
291
+ self.config.config[setting] = new_value
292
+ self.config.save_config()
293
+ self.console.print("[green]✓ Configuration updated.[/]")
294
+ else:
295
+ self.console.print("[red]Invalid setting name.[/]")
296
+
297
+ def main():
298
+ """Entry point for the WRD CLI."""
299
+ if len(sys.argv) > 1 and sys.argv[1] == 'shell':
300
+ WRDShell().shell()
301
+ elif len(sys.argv) > 1 and sys.argv[1] == 'init':
302
+ WRDShell().init_project(sys.argv[2] if len(sys.argv) > 2 else None)
303
+ else:
304
+ console = Console()
305
+ console.print("WRD - WRonai Development Tool")
306
+ console.print("\nUsage:")
307
+ console.print(" wrd shell - Start interactive shell")
308
+ console.print(" wrd init [path] - Initialize a new project")
309
+ console.print(" wrd --help - Show this help")
310
+
311
+ if __name__ == "__main__":
312
+ main()
wrd/py.typed ADDED
File without changes
@@ -0,0 +1,163 @@
1
+ """Template manager for WRD."""
2
+
3
+ import os
4
+ import shutil
5
+ from pathlib import Path
6
+ from typing import Dict, Any, Optional, List
7
+ import yaml
8
+ import jinja2
9
+ import importlib.resources as pkg_resources
10
+ from . import templates
11
+
12
+ class TemplateManager:
13
+ """Manages project templates for different languages and project types."""
14
+
15
+ def __init__(self):
16
+ self.templates_dir = Path(pkg_resources.files(templates))
17
+ self.available_templates = self._discover_templates()
18
+
19
+ def _discover_templates(self) -> Dict[str, Dict[str, Any]]:
20
+ """Discover all available templates in the templates directory."""
21
+ templates = {}
22
+
23
+ if not self.templates_dir.exists():
24
+ return {}
25
+
26
+ for template_dir in self.templates_dir.iterdir():
27
+ if template_dir.is_dir() and (template_dir / 'project.yml').exists():
28
+ try:
29
+ with open(template_dir / 'project.yml', 'r') as f:
30
+ config = yaml.safe_load(f)
31
+ templates[config['name']] = {
32
+ 'config': config,
33
+ 'path': template_dir
34
+ }
35
+ except (yaml.YAMLError, KeyError) as e:
36
+ print(f"Error loading template {template_dir.name}: {e}")
37
+
38
+ return templates
39
+
40
+ def get_template(self, name: str) -> Optional[Dict[str, Any]]:
41
+ """Get a template by name."""
42
+ return self.available_templates.get(name)
43
+
44
+ def list_templates(self) -> List[str]:
45
+ """List all available template names."""
46
+ return list(self.available_templates.keys())
47
+
48
+ def create_project(
49
+ self,
50
+ template_name: str,
51
+ project_path: Path,
52
+ context: Dict[str, Any],
53
+ overwrite: bool = False
54
+ ) -> bool:
55
+ """Create a new project from a template.
56
+
57
+ Args:
58
+ template_name: Name of the template to use
59
+ project_path: Path where the project should be created
60
+ context: Dictionary with template variables
61
+ overwrite: Whether to overwrite existing files
62
+
63
+ Returns:
64
+ bool: True if project was created successfully, False otherwise
65
+ """
66
+ template = self.get_template(template_name)
67
+ if not template:
68
+ print(f"Error: Template '{template_name}' not found")
69
+ return False
70
+
71
+ project_path = project_path.absolute()
72
+
73
+ # Create project directory if it doesn't exist
74
+ project_path.mkdir(parents=True, exist_ok=True)
75
+
76
+ # Check if directory is empty or overwrite is allowed
77
+ if any(project_path.iterdir()) and not overwrite:
78
+ print(f"Error: Directory {project_path} is not empty. Use --overwrite to ignore.")
79
+ return False
80
+
81
+ # Process template files
82
+ config = template['config']
83
+ template_dir = template['path']
84
+
85
+ # Create directories
86
+ for dir_path in config.get('directories', []):
87
+ dir_path = project_path / self._render_template(dir_path, context)
88
+ dir_path.mkdir(parents=True, exist_ok=True)
89
+
90
+ # Create files
91
+ for file_spec in config.get('files', []):
92
+ if isinstance(file_spec, dict):
93
+ file_path = file_spec['path']
94
+ content = file_spec.get('content')
95
+ template_file = file_spec.get('template')
96
+ else:
97
+ file_path = file_spec
98
+ content = None
99
+ template_file = None
100
+
101
+ # Render the file path
102
+ file_path = self._render_template(file_path, context)
103
+ dest_path = project_path / file_path
104
+
105
+ # Create parent directories if they don't exist
106
+ dest_path.parent.mkdir(parents=True, exist_ok=True)
107
+
108
+ # Write file content
109
+ if content is not None:
110
+ # Use direct content
111
+ dest_path.write_text(self._render_template(content, context))
112
+ elif template_file is not None:
113
+ # Use template file
114
+ template_path = template_dir / template_file
115
+ if template_path.exists():
116
+ template_content = template_path.read_text()
117
+ rendered = self._render_template(template_content, context)
118
+ dest_path.write_text(rendered)
119
+ else:
120
+ # Create empty file
121
+ dest_path.touch()
122
+
123
+ # Run post-create commands
124
+ for cmd in config.get('post_create_commands', []):
125
+ cmd = self._render_template(cmd, context)
126
+ os.system(f"cd {project_path} && {cmd}")
127
+
128
+ return True
129
+
130
+ def _render_template(self, template_str: str, context: Dict[str, Any]) -> str:
131
+ """Render a template string with the given context."""
132
+ return jinja2.Template(template_str).render(**context)
133
+
134
+ def get_template_variables(self, template_name: str) -> Dict[str, Any]:
135
+ """Get the required variables for a template."""
136
+ template = self.get_template(template_name)
137
+ if not template:
138
+ return {}
139
+
140
+ # Extract variables from template files
141
+ variables = set()
142
+
143
+ # Check config files
144
+ for file_spec in template['config'].get('files', []):
145
+ if isinstance(file_spec, dict):
146
+ content = file_spec.get('content', '')
147
+ template_file = file_spec.get('template')
148
+
149
+ if template_file:
150
+ template_path = template['path'] / template_file
151
+ if template_path.exists():
152
+ content = template_path.read_text()
153
+
154
+ # Simple variable extraction ({{ variable }})
155
+ import re
156
+ variables.update(re.findall(r'\{\{\s*([^\s}]+)\s*\}\}', content))
157
+
158
+ return {var: "" for var in variables}
159
+
160
+
161
+ def get_template_manager() -> TemplateManager:
162
+ """Get a template manager instance."""
163
+ return TemplateManager()
@@ -0,0 +1,4 @@
1
+ """WRD templates package."""
2
+
3
+ # This file makes the templates directory a Python package
4
+ # so we can use importlib.resources to access template files.
@@ -0,0 +1,33 @@
1
+ # {{ project_name }}
2
+
3
+ {{ description }}
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install -e .
9
+ ```
10
+
11
+ ## Development
12
+
13
+ Install development dependencies:
14
+
15
+ ```bash
16
+ pip install -e ".[dev]"
17
+ ```
18
+
19
+ ## Running Tests
20
+
21
+ ```bash
22
+ pytest
23
+ ```
24
+
25
+ ## Building the Package
26
+
27
+ ```bash
28
+ python -m build
29
+ ```
30
+
31
+ ## License
32
+
33
+ This project is licensed under the {{ license }} License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,51 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual Environment
24
+ venv/
25
+ env/
26
+
27
+ # IDE
28
+ .idea/
29
+ .vscode/
30
+ *.swp
31
+ *.swo
32
+
33
+ # Testing
34
+ .coverage
35
+ htmlcov/
36
+ .pytest_cache/
37
+
38
+ # Distribution
39
+ *.whl
40
+ *.tar.gz
41
+
42
+ # Local development
43
+ .env
44
+ .env.local
45
+
46
+ # Logs
47
+ *.log
48
+
49
+ # OS specific
50
+ .DS_Store
51
+ Thumbs.db
@@ -0,0 +1,39 @@
1
+ # Python project template configuration
2
+ name: python
3
+ version: 1.0.0
4
+ description: A Python project template for WRD
5
+
6
+ # Files to create with their content templates
7
+ files:
8
+ - path: pyproject.toml
9
+ template: pyproject.toml.j2
10
+ - path: README.md
11
+ template: README.md.j2
12
+ - path: .gitignore
13
+ template: gitignore.j2
14
+ - path: src/{{ package_name }}/__init__.py
15
+ content: |
16
+ """{{ project_name }} - {{ description }}"""
17
+
18
+ __version__ = "0.1.0"
19
+ - path: tests/__init__.py
20
+ content: "# Test package"
21
+ - path: tests/test_basic.py
22
+ content: |
23
+ """Basic tests for {{ project_name }}."""
24
+
25
+ def test_import():
26
+ """Test that the package can be imported."""
27
+ import {{ package_name }}
28
+ assert {{ package_name }} is not None
29
+
30
+ # Directories to create
31
+ directories:
32
+ - src/{{ package_name }}
33
+ - tests
34
+
35
+ # Commands to run after project creation
36
+ post_create_commands:
37
+ - git init
38
+ - git add .
39
+ - git commit -m "Initial commit"
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "{{ package_name }}"
7
+ version = "0.1.0"
8
+ description = "{{ description }}"
9
+ authors = [
10
+ { name = "{{ author_name }}", email = "{{ author_email }}" }
11
+ ]
12
+ readme = "README.md"
13
+ requires-python = ">=3.8"
14
+ license = { text = "{{ license }}" }
15
+
16
+ [project.optional-dependencies]
17
+ dev = [
18
+ "pytest>=6.0",
19
+ "black>=21.12b0",
20
+ "isort>=5.10.1",
21
+ "mypy>=0.910",
22
+ "pytest-cov>=2.0"
23
+ ]
24
+
25
+ [tool.black]
26
+ line-length = 88
27
+ target-version = ["py38"]
28
+
29
+ [tool.isort]
30
+ profile = "black"
31
+ line_length = 88
32
+
33
+ [tool.pytest.ini_options]
34
+ testpaths = ["tests"]
35
+ python_files = "test_*.py"