Skip to content

Instantly share code, notes, and snippets.

@niftycode
Last active September 5, 2025 15:04
Show Gist options
  • Save niftycode/fd1442c624798c5f2773db0b443827cc to your computer and use it in GitHub Desktop.
Save niftycode/fd1442c624798c5f2773db0b443827cc to your computer and use it in GitHub Desktop.
Shell script to create a Python project
#!/bin/bash -
#===============================================================================
#
# FILE: createPythonProject.sh
#
# USAGE: ./createPythonProject.sh
#
# DESCRIPTION: Create a new Python project
#
# VERSION: 2.2
# REQUIREMENTS: ---
# BUGS: ---
# NOTES: This script creates a pyproject.toml. In addition, the configuration
# file logging.ini is created for the logging module.
#
# AUTHOR: @niftycode
# ORGANIZATION:
# CREATED: December 10th, 2021
# REVISION: September 5th, 2025
#===============================================================================
set -o nounset # Treat unset variables as an error
echo "Enter a project name:"
read name
mkdir $name
cd $name
# Create the subfolders 'docs' and 'tests'
mkdir docs
mkdir tests
# Create __init__.py file in the 'tests' folder
cd tests
touch __init__.py
cd ..
# Create pyproject.toml file
cat > pyproject.toml << EOF
[build-system]
requires = ["setuptools>=70.0"]
build-backend = "setuptools.build_meta"
[project]
name = "$name"
version = "0.1.0"
description = "Project description"
readme = "README.md"
requires-python = ">=3.12"
license = { text = "MIT" }
authors = [
{ name = "Your Name", email = "[email protected]" }
]
urls = {"Homepage" = "https://github.com/username/project_name"}
classifiers = [
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython"
]
dependencies = []
[project.scripts]
$name = "src.main:main"
[tool.setuptools]
zip-safe = false
packages = { find = { exclude = ["tests"] } }
EOF
# Create requirements.txt file
cat > requirements.txt << EOF
setuptools
wheel
build
flake8
black
mypy
pytest
pytest-cov
EOF
# Create .flake8 file
cat > .flake8 << EOF
[flake8]
extend-ignore = E203, W503
max-line-length = 120
exclude =
.git,
__pycache__,
docs/source/conf.py,
build,
dist
max-complexity = 10
EOF
# Create logging.ini
cat > logging.ini << EOF
[loggers]
keys=root
[handlers]
keys=stream_handler
[formatters]
keys=formatter
[logger_root]
level=DEBUG
handlers=stream_handler
[handler_stream_handler]
class=StreamHandler
level=DEBUG
formatter=formatter
args=(sys.stderr,)
[formatter_formatter]
format=%(asctime)s %(name)-12s %(levelname)-8s %(message)s
EOF
# Create empty Changelog and Contributing files
touch Changelog.md
touch CONTRIBUTING.md
# Create Readme file
cat > README.md << EOF
# $name
EOF
# Create .gitignore file
cat > .gitignore << EOF
#############################
#### Configuration Files ####
#############################
.DS_Store
.vscode
.idea
.fleet
#############################
### Build Files & Folders ###
#############################
dist/
build/
wheels/
*.egg-info/
__pycache__/
#############################
### venv & Code Coverage ####
#############################
.venv/
.coverage
/htmlcov
#############################
############ uv #############
#############################
.python-version
uv.lock
#############################
########### Misc ############
#############################
.mypy_cache
.pytest_cache
EOF
# Create subfolder with the name of the project and
# create __init__.py and main.py file
echo
read -p "Should the project directory be named 'src'? (Y/n)" dir_name
dir_name=${dir_name:-Y}
if [ $dir_name = "Y" ]
then
mkdir src
echo "[+] src created"
cd src
else
mkdir $name
echo "[+] $name created"
cd $name
fi
touch __init__.py
month=$(date "+%B")
day=$(date "+%d")
year=$(date "+%Y")
# Create main.py file
cat > main.py << EOF
#!/usr/bin/env python3
"""
Description goes here...
Version: 1.0
Python 3.12+
Date created: $month $day, $year
Date modified: -
"""
import logging
from logging.config import fileConfig
fileConfig("logging.ini")
logger = logging.getLogger()
def main() -> None:
pass
if __name__ == '__main__':
main()
EOF
cd ..
# Create mypy.ini file
echo
read -p "Do you need a mypy.ini file? (Y/n)" mypy
mypy=${mypy:-Y}
if [ $mypy = "Y" ]
then
touch mypy.ini
echo "[+] Created mypy.ini file!"
else
echo "[-] mypy.ini will not be created"
cd ..
fi
if test -f "mypy.ini"; then
echo "[mypy]" >> mypy.ini
echo "disable_error_code = import-untyped" >> mypy.ini
echo "ignore_missing_imports = True" >> mypy.ini
fi
# Create pytest.ini file
echo
read -p "Do you need a pytest.ini file? (Y/n)" pytest
pytest=${pytest:-Y}
if [ $pytest = "Y" ]
then
touch pytest.ini
echo "[+] Created pytest.ini file!"
else
echo "[-] pytest.ini will not be created"
fi
if test -f "pytest.ini"; then
echo "[pytest]" >> pytest.ini
echo "norecursedirs = .* src *.egg dist build" >> pytest.ini
echo "addopts = -rsxX -l --tb=short --strict-markers" >> pytest.ini
fi
# Create virtual environment
echo
read -p "Do you need a virtual environment? (Y/n)" venv
venv=${venv:-Y}
if [ $venv = "Y" ]
then
echo "[+] Creating a virtual environment..."
python3 -m venv .venv
echo
echo "Done!"
echo
echo "Start the virtual environment using 'source .venv/bin/activate'."
else
echo "[-] A virtual environment will not be created!"
fi
echo
echo "-----------------------"
echo "All done! Exit program."
echo "-----------------------"
echo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment