commit 8468e3b6c7fe4e1a89ae06f8b87e1e29c132cb84 Author: grym Date: Sat Jan 6 10:44:48 2024 -0500 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb45954 --- /dev/null +++ b/.gitignore @@ -0,0 +1,857 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/images +# Edit at https://www.toptal.com/developers/gitignore?templates=images + +### Images ### +# JPEG +*.jpg +*.jpeg +*.jpe +*.jif +*.jfif +*.jfi + +# JPEG 2000 +*.jp2 +*.j2k +*.jpf +*.jpx +*.jpm +*.mj2 + +# JPEG XR +*.jxr +*.hdp +*.wdp + +# Graphics Interchange Format +*.gif + +# RAW +*.raw + +# Web P +*.webp + +# Portable Network Graphics +*.png + +# Animated Portable Network Graphics +*.apng + +# Multiple-image Network Graphics +*.mng + +# Tagged Image File Format +*.tiff +*.tif + +# Scalable Vector Graphics +*.svg +*.svgz + +# Portable Document Format +*.pdf + +# X BitMap +*.xbm + +# BMP +*.bmp +*.dib + +# ICO +*.ico + +# 3D Images +*.3dm +*.max + +# End of https://www.toptal.com/developers/gitignore/api/images +# Created by https://www.toptal.com/developers/gitignore/api/direnv +# Edit at https://www.toptal.com/developers/gitignore?templates=direnv + +### direnv ### +.direnv +.envrc + +# End of https://www.toptal.com/developers/gitignore/api/direnv + +# Created by https://www.toptal.com/developers/gitignore/api/visualstudio +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudio + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +### VisualStudio Patch ### +# Additional files built by Visual Studio + +# End of https://www.toptal.com/developers/gitignore/api/visualstudio + +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode + +# Created by https://www.toptal.com/developers/gitignore/api/vim +# Edit at https://www.toptal.com/developers/gitignore?templates=vim + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +# End of https://www.toptal.com/developers/gitignore/api/vim + +# Created by https://www.toptal.com/developers/gitignore/api/emacs +# Edit at https://www.toptal.com/developers/gitignore?templates=emacs + +### Emacs ### +# -*- mode: gitignore; -*- +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +auto-save-list +tramp +.\#* + +# Org-mode +.org-id-locations +*_archive + +# flymake-mode +*_flymake.* + +# eshell files +/eshell/history +/eshell/lastdir + +# elpa packages +/elpa/ + +# reftex files +*.rel + +# AUCTeX auto folder +/auto/ + +# cask packages +.cask/ +dist/ + +# Flycheck +flycheck_*.el + +# server auth directory +/server/ + +# projectiles files +.projectile + +# directory configuration +.dir-locals.el + +# network security +/network-security.data + + +# End of https://www.toptal.com/developers/gitignore/api/emacs + +# Created by https://www.toptal.com/developers/gitignore/api/jetbrains+all +# Edit at https://www.toptal.com/developers/gitignore?templates=jetbrains+all + +### JetBrains+all ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains+all Patch ### +# Ignore everything but code style settings and run configurations +# that are supposed to be shared within teams. + +.idea/* + +!.idea/codeStyles +!.idea/runConfigurations + +# End of https://www.toptal.com/developers/gitignore/api/jetbrains+all + +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### Python Patch ### +# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration +poetry.toml + +# ruff +.ruff_cache/ + +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c4f0a02 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,44 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +fail_fast: false +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-json + - id: check-merge-conflict + - id: debug-statements + - id: detect-private-key + - id: fix-byte-order-marker + - id: check-yaml + - id: mixed-line-ending + args: [--fix=lf] + - id: check-added-large-files + args: [--maxkb=5120] +- repo: https://github.com/psf/black + rev: 23.10.0 + hooks: + - id: black + name: Formatting + args: ["dev_utils"] +- repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + name: Import order + args: ["--profile=black", "dev_utils"] +- repo: https://github.com/PyCQA/autoflake + rev: v2.2.1 + hooks: + - id: autoflake + name: Import pruning + args: ["-i", "-r","--remove-unused-variables","--ignore-init-module-imports"] +- repo: local + hooks: + - id: breakpoint_statement + name: Check that breakpoint() isn't in source code + types: [python] + entry: 'breakpoint()' + language: pygrep diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0893cb2 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +SHELL := /bin/bash +.PHONY: all venv + +VENV_DIR=$$(pwd)/venv +VENV_PYTHON=$(VENV_DIR)/bin/python +VENV_BIN=$(VENV_DIR)/bin + +venv: + @echo making venv at $(VENV_DIR) + @python -m venv $(VENV_DIR) + @$(VENV_PYTHON) -m pip install --upgrade pip setuptools wheel + +install: venv + @$(VENV_PYTHON) -m pip install --upgrade -e '.[dev]' + @$(VENV_PYTHON) -m pre_commit install + +build: install + @$(VENV_PYTHON) -m build + +clean: + @rm -rf ./dist + +test: install + @$(VENV_BIN)/pytest --cov=dev_utils ./tests + +docs: install + @$(VENV_BIN)/cog -r ./README.md + @$(VENV_BIN)/scriv collect diff --git a/README.md b/README.md new file mode 100644 index 0000000..7fd7e26 --- /dev/null +++ b/README.md @@ -0,0 +1,107 @@ +# dev-utils +This is a collection of utility scripts for handling common tasks on linux/mac +machines. + + + +# Command line use + +## dev-utils +```text + + Usage: dev-utils [OPTIONS] COMMAND [ARGS]... + + A collection of provisioning tools for common tasks during (mostly python) development. + +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --version Show the version and exit. │ +│ --install-completion Install completion for the current shell. │ +│ --show-completion Show completion for the current shell, to copy it or customize the installation. │ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Commands ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ direnv Emit the contents of a .envrc file suitable for python development. │ +│ direnv-init Configure the user's direnv rc file │ +│ precommit If precommit is available, run it until it succeeds or `max_tries` is exceeded. │ +│ venv Make a venv │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + +``` + + + +## dev-utils precommit +```text + + Usage: dev-utils precommit [OPTIONS] + + If precommit is available, run it until it succeeds or `max_tries` is exceeded. + +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --max-tries INTEGER [default: 3] │ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + +``` +## dev-utils venv +```text + + Usage: dev-utils venv [OPTIONS] + + Make a venv + +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --base-python TEXT [default: None] │ +│ --name TEXT [default: venv] │ +│ --do-update --no-do-update [default: do-update] │ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + +``` +## dev-utils direnv-init +```text + + Usage: dev-utils direnv-init [OPTIONS] + + Configure the user's direnv rc file + +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --replace --no-replace [default: no-replace] │ +│ --target-dir PATH [default: /Users/gvoysey/.config/direnv] │ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + +``` +## dev-utils direnv +```text + + Usage: dev-utils direnv [OPTIONS] + + Emit the contents of a .envrc file suitable for python development. + +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --python-version TEXT [default: None] │ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + +``` + diff --git a/dev_utils/__init__.py b/dev_utils/__init__.py new file mode 100644 index 0000000..c2e52b6 --- /dev/null +++ b/dev_utils/__init__.py @@ -0,0 +1,21 @@ +import logging +from importlib.metadata import version + +import coloredlogs + +__version__ = version(__name__) + +logger = logging.getLogger(__name__) + +print_handler = logging.StreamHandler() +logfmt = ( + "%(asctime)s [%(levelname)-8s] %(module)s.%(funcName)s(+L%(lineno)-3d): %(message)s" +) +print_handler.setFormatter(coloredlogs.ColoredFormatter(fmt=logfmt)) +logging.basicConfig( + level="INFO", + format=logfmt, + handlers=[ + print_handler, + ], +) diff --git a/dev_utils/__main__.py b/dev_utils/__main__.py new file mode 100644 index 0000000..459f30e --- /dev/null +++ b/dev_utils/__main__.py @@ -0,0 +1,134 @@ +import importlib.resources +import shutil +import subprocess +import sys +import textwrap +import typing as t +from pathlib import Path + +import typer +from typer.main import get_group + +from dev_utils import __version__ + +IS_WINDOWS = sys.platform == "win32" + +app = typer.Typer( + context_settings={ + "max_content_width": 80, + } +) + + +def version_callback(value: bool): + if value: + typer.echo(f"{__version__}") + raise typer.Exit() + + +@app.callback() +def main( + version: t.Optional[bool] = typer.Option( + None, + "--version", + callback=version_callback, + is_eager=True, + help="Show the version and exit.", + ), +): + """A collection of provisioning tools for common tasks during (mostly python) development.""" + + +@app.command() +def precommit(max_tries: int = typer.Option(3)): + """If precommit is available, run it until it succeeds or `max_tries` is exceeded.""" + if precommit := shutil.which("pre-commit"): + cmdlist = [precommit, "run", "--all-files"] + while max_tries > 0: + res = subprocess.run(cmdlist) + if res.returncode == 0: + break + max_tries -= 1 + + +@app.command() +def venv( + base_python: t.Optional[str] = typer.Option(None), + name: str = typer.Option("venv"), + do_update: bool = typer.Option(True), +): + """Make a venv""" + venv_location = Path.cwd().joinpath(name) + cmdlist = [ + "python" if not base_python else base_python, + "-m", + "venv", + str(venv_location), + ] + res = subprocess.run(cmdlist) + if not res.returncode == 0: + raise typer.Exit() + if not do_update: + raise typer.Exit() + if IS_WINDOWS: + venv_python = str(venv_location.joinpath("Scripts", "python.exe")) + else: + venv_python = str(venv_location.joinpath("bin", "python")) + cmdlist = [ + venv_python, + "-m", + "pip", + "install", + "--upgrade", + "pip", + "setuptools", + "wheel", + ] + subprocess.run(cmdlist) + raise typer.Exit() + + +@app.command() +def direnv_init( + replace: bool = typer.Option(False), + target_dir: Path = typer.Option(Path.home().joinpath(".config", "direnv")), +): + """Configure the user's direnv rc file""" + if IS_WINDOWS: + typer.echo("Direnv is not available on windows") + raise typer.Exit() + + open_mode = "w" if replace else "a" + ref = importlib.resources.files("dev_utils.resources").joinpath("direnvrc.txt") + target_dir.mkdir(exist_ok=True, parents=True) + target_file = target_dir.joinpath("direnvrc") + with importlib.resources.as_file(ref) as datapath: + target_file.open(open_mode).write(datapath.read_text()) + typer.echo( + f"{'Wrote' if replace else 'Appended'} direnvrc contents to {target_file}" + ) + raise typer.Exit() + + +@app.command() +def direnv(python_version: t.Optional[str] = typer.Option(None)): + """Emit the contents of a .envrc file suitable for python development.""" + if IS_WINDOWS: + typer.echo("Direnv is not available on windows") + raise typer.Exit() + + version_string = python_version if python_version else "" + typer.echo( + textwrap.dedent( + f"""\ + export VIRTUAL_ENV=./venv + layout python-venv {version_string} + export PYTHONBREAKPOINT="pudb.set_trace" + """ + ) + ) + raise typer.Exit() + + +# must be below all @app.command()s. +_subcommand_names = list(typer.main.get_command(app).commands) diff --git a/dev_utils/resources/__init__.py b/dev_utils/resources/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dev_utils/resources/direnvrc.txt b/dev_utils/resources/direnvrc.txt new file mode 100644 index 0000000..636f770 --- /dev/null +++ b/dev_utils/resources/direnvrc.txt @@ -0,0 +1,27 @@ +realpath() { + [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}" +} +layout_python-venv() { + local python=${1:-python3} + [[ $# -gt 0 ]] && shift + unset PYTHONHOME + if [[ -n $VIRTUAL_ENV ]]; then + VIRTUAL_ENV=$(realpath "${VIRTUAL_ENV}") + else + local python_version + python_version=$("$python" -c "import platform; print(platform.python_version())") + if [[ -z $python_version ]]; then + log_error "Could not detect Python version" + return 1 + fi + VIRTUAL_ENV=$PWD/.direnv/python-venv-$python_version + fi + export VIRTUAL_ENV + if [[ ! -d $VIRTUAL_ENV ]]; then + log_status "no venv found; creating $VIRTUAL_ENV" + "$python" -m venv "$VIRTUAL_ENV" + fi + + PATH="${VIRTUAL_ENV}/bin:${PATH}" + export PATH +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6e10098 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,46 @@ +[build-system] +requires = ["setuptools>=61.0.0", "wheel", "versioningit>=2.1.0"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.packages] +find = {namespaces = false} +[tool.setuptools.package-data] +"*" = ["*.txt"] + + +[project] +name = "dev-utils" +description = "a collection of provisioning tools for common tasks during python development" +requires-python = ">=3.9" +dynamic = ["version"] +authors=[{name="grym", email="grym@ctrl-c.club"}] +dependencies = [ "typer[all]", # CLI argument parser + "coloredlogs", # pretty log formatting + ] + +[project.optional-dependencies] +dev = ["pre-commit", # automatic formatter/tool runner on git commit + "pytest", # unit testing framework + "pytest-cov", # test coverage metrics + "ipython", # featureful REPL + "pudb", # TUI debugger + "build", # wheel-maker for setuptools + "twine", # wheel-publisher + "scriv", # changelog management + "cogapp", # programmatically update documents + ] + + +[project.scripts] +dev-utils = "dev_utils:__main__.app" + +[tool.versioningit] +default-version = "0+unknown" + +[tool.versioningit.format] +distance = "{version}+{distance}.{vcs}{rev}" +dirty = "{version}+{distance}.{vcs}{rev}.dirty" +distance-dirty = "{version}+{distance}.{vcs}{rev}.dirty" + +[tool.scriv] +format="md" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_deleteme.py b/tests/test_deleteme.py new file mode 100644 index 0000000..0c3697a --- /dev/null +++ b/tests/test_deleteme.py @@ -0,0 +1,2 @@ +def test_delete_this_test(): + assert True