Skip to content

Commit 3ebefbe

Browse files
Update default template (#7)
* Update default template * add wf * add reqs * Redo setup script
1 parent 7198d5d commit 3ebefbe

File tree

20 files changed

+326
-626
lines changed

20 files changed

+326
-626
lines changed

.github/workflows/lint.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: Lint
2+
on: [push, pull_request]
3+
4+
jobs:
5+
lint:
6+
runs-on: ubuntu-latest
7+
steps:
8+
- uses: actions/checkout@v4
9+
- uses: actions/setup-python@v4
10+
- run: pip install -r requirements/common.txt
11+
- run: ruff check --output-format=github .

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
__pycache__/
33
*.py[cod]
44
*$py.class
5+
.ruff_cache/
56

67
# Distribution / packaging
78
.Python

.pre-commit-config.yaml

Lines changed: 0 additions & 49 deletions
This file was deleted.

README.md

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,24 @@
11
# Cookiecutter Python Package
22

3-
A [Cookiecutter](https://github.com/cookiecutter/cookiecutter) template for a Python package with Poetry as the dependency manager.
3+
A [Cookiecutter](https://github.com/cookiecutter/cookiecutter) blank template for a project.
44

55
**NOTE:** Only Python 3.8+ is supported.
66

77
---
88

99
## Features
10-
- Hooks: Pre-commit
11-
- Formatters and Linters: Black, Flake8, Flake8-bugbear, Isort, and Lizard
10+
- Package manager: pip
11+
- Formatters and Linters: Ruff
1212
- Testing Frameworks (Optional): Pytest, Coverage, and CovDefaults
1313

1414
## Usage
1515

16-
- Since this template uses Poetry as the dependancy manager, install poetry from `https://python-poetry.org/docs/#installation`
17-
18-
- Install the `cookiecutter` library.
19-
20-
```python
21-
pip install cookiecutter
22-
```
23-
OR
24-
```python
25-
python -m pip install cookiecutter
26-
```
27-
28-
- Run the command:
29-
```python
30-
cookiecutter https://github.com/gurashish1singh/cookiecutter-python.git
31-
```
32-
OR
33-
```python
34-
python -m cookiecutter https://github.com/gurashish1singh/cookiecutter-python.git
35-
```
16+
- bash setup.sh
17+
3618
- This template uses post-project generation hooks to:
3719
- Initialize a git repository (with default branch as main), IF the working directory is not already a git repository.
3820

3921
**NOTE**: You will have to create a repositry on remote if it doesn't already exist before running the cookiecutter command.
40-
- Create a Poetry virtualenv
41-
- Install all dependencies
42-
- Install the pre-commit and pre-push hooks
22+
- Create a python virtual environment and activate it
23+
- Install ruff
24+
- Include an initial lint github workflow

cookiecutter.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"project_name": "Boilerplate Template",
66
"github_repo_name": "{{ cookiecutter.project_name.lower().replace(' ', '-') }}",
77
"project_slug": "{{ cookiecutter.project_name.lower().replace(' ', '_').replace('-', '_') }}",
8-
"project_short_description": "Minimal python cookiecutter template with Poetry as the dependency manager and Black, Isort, Flake8, Flake8-bugbear as the linters.",
8+
"project_short_description": "Minimal python cookiecutter template with pip as the package manager and Ruff as the linting tool.",
99
"version": "1.0.0",
10-
"pytest": ["y", "n"]
10+
"pytest": ["n", "y"]
1111
}

hooks/post_gen_project.py

Lines changed: 101 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1-
#!/usr/bin/env python
21
from __future__ import annotations
32

3+
import os
4+
import pathlib
45
import shlex
56
import subprocess
67
import sys
8+
import venv
79
from pathlib import Path
810

911
ERROR_MSG = "Error occured while running command"
1012
PRETTY_LINES = "*" * 80
13+
PROJECT_NAME = "{{ cookiecutter.project_slug }}"
14+
# TODO: Can allow user to pass it in through cookiecutter.json
15+
VENV_NAME = ".venv"
16+
VIRTUAL_ENV = "VIRTUAL_ENV"
17+
18+
SUBPROCESS_PARAMS = {
19+
"stdout": subprocess.PIPE,
20+
"stderr": subprocess.PIPE,
21+
"check": True,
22+
}
1123

1224

1325
def main() -> int:
@@ -19,7 +31,12 @@ def main() -> int:
1931

2032
if return_code_one == 0:
2133
return_code_two = setup_environment()
22-
return 0 or return_code_one or return_code_two
34+
35+
if return_code_two == 0:
36+
return_code_three = _rename_pyproject_toml_file()
37+
print(f"New project {PROJECT_NAME!r} is setup.")
38+
39+
return 0 or return_code_one or return_code_two or return_code_three
2340

2441

2542
def initialize_git() -> int:
@@ -30,14 +47,14 @@ def initialize_git() -> int:
3047
),
3148
"init_git": (
3249
shlex.split("git init"),
33-
"Initializing an empty git repository locally. You will have to create a repo "
50+
"Initializing an empty git repository locally. You will have to create a repository "
3451
"on remote.\n",
3552
),
3653
}
3754
for cmds, message in COMMANDS_AND_MESSAGE.values():
3855
print(message)
3956
try:
40-
subprocess.run(cmds, check=True)
57+
subprocess.run(cmds, **SUBPROCESS_PARAMS)
4158
except subprocess.CalledProcessError as e:
4259
print(ERROR_MSG, e)
4360
return e.returncode
@@ -46,26 +63,87 @@ def initialize_git() -> int:
4663

4764

4865
def setup_environment() -> int:
66+
return_code = 0
67+
try:
68+
# Always create a new environment in the project dir
69+
python_venv_path = _create_new_environment()
70+
system_type, python_executable_path, python_activate_script_path = _get_python_paths(venv_path=python_venv_path)
71+
_activate_environment(system_type, python_activate_script_path)
72+
except Exception:
73+
return_code = -1
74+
return_code = _install_requirements(python_executable_path)
75+
return 0 or return_code
4976

50-
COMMANDS_AND_MESSAGE = {
51-
"install_poetry": (
52-
shlex.split("poetry install"),
53-
f"\n{PRETTY_LINES}\nInstalling poetry virtual environment",
54-
),
55-
"install_pre_commit": (
56-
shlex.split(
57-
"poetry run pre-commit install --hook-type pre-commit --hook-type pre-push"
58-
),
59-
f"\n{PRETTY_LINES}\nInstalling pre-commit hooks",
60-
),
61-
}
62-
for cmds, message in COMMANDS_AND_MESSAGE.values():
63-
print(message)
64-
try:
65-
subprocess.run(cmds, check=True)
66-
except subprocess.CalledProcessError as e:
67-
print(ERROR_MSG, e)
68-
return e.returncode
77+
78+
def _create_new_environment() -> str:
79+
parent_dir = pathlib.Path(os.getcwd()).parent.resolve()
80+
project_name = PROJECT_NAME.strip()
81+
python_venv_path = str(parent_dir / project_name / VENV_NAME)
82+
83+
print(PRETTY_LINES)
84+
print(f"Attempting to create a new virtual env at {python_venv_path}")
85+
try:
86+
venv.create(env_dir=python_venv_path, with_pip=True)
87+
except Exception:
88+
print("An unexpected error has occured")
89+
raise
90+
91+
print(f"Successfully created virtualenv at: {python_venv_path!r}")
92+
return python_venv_path
93+
94+
95+
def _get_python_paths(venv_path: str) -> tuple[str, str, str]:
96+
sys_type = sys.platform
97+
if sys_type == "win32":
98+
python_executable_path = pathlib.Path(venv_path, "Scripts", "python.exe")
99+
activate_script_path = pathlib.Path(venv_path, "Scripts", "activate")
100+
elif sys_type in ("darwin", "linux", "linux2"):
101+
python_executable_path = pathlib.Path(venv_path, "bin", "python")
102+
activate_script_path = pathlib.Path(venv_path, "bin", "activate")
103+
else:
104+
raise OSError(f"Unsupported platform: {sys_type!r}")
105+
return sys_type, str(python_executable_path), str(activate_script_path)
106+
107+
108+
def _activate_environment(system_type: str, activate_script_path: str) -> None:
109+
if system_type == "win32":
110+
subprocess.call(["cmd.exe", "c", activate_script_path])
111+
elif system_type in ("darwin", "linux", "linux2"):
112+
subprocess.call(f"source {activate_script_path}", shell=True)
113+
114+
print("Successfully activated virtualenv.")
115+
print(f"\n{PRETTY_LINES}")
116+
117+
118+
def _install_requirements(python_executable_path: str) -> int:
119+
print("Installing all dependencies from the requirements.txt file.\n")
120+
try:
121+
subprocess.run(
122+
[python_executable_path, "-m", "pip", "install", "-r", "requirements.txt"],
123+
**SUBPROCESS_PARAMS,
124+
)
125+
if "{{ cookiecutter.project_slug }}":
126+
subprocess.run(
127+
[python_executable_path, "-m", "pip", "install", "-r", "dev-requirements.txt"],
128+
**SUBPROCESS_PARAMS,
129+
)
130+
except subprocess.CalledProcessError as e:
131+
print(ERROR_MSG, e)
132+
return e.returncode
133+
134+
print(PRETTY_LINES)
135+
return 0
136+
137+
138+
def _rename_pyproject_toml_file() -> int:
139+
try:
140+
os.rename(
141+
"template-pyproject.toml",
142+
"pyproject.toml",
143+
)
144+
except subprocess.CalledProcessError as e:
145+
print(ERROR_MSG, e)
146+
return e.returncode
69147

70148
return 0
71149

0 commit comments

Comments
 (0)