bashers 0.1.0__tar.gz

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.
@@ -0,0 +1,33 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.so
5
+ .Python
6
+ build/
7
+ develop-eggs/
8
+ dist/
9
+ downloads/
10
+ eggs/
11
+ .eggs/
12
+ lib/
13
+ lib64/
14
+ parts/
15
+ sdist/
16
+ var/
17
+ wheels/
18
+ *.egg-info/
19
+ .installed.cfg
20
+ *.egg
21
+ MANIFEST
22
+ .venv/
23
+ venv/
24
+ ENV/
25
+ env/
26
+ .pytest_cache/
27
+ .coverage
28
+ htmlcov/
29
+ .tox/
30
+ .hypothesis/
31
+ .mypy_cache/
32
+ .dmypy.json
33
+ dmypy.json
bashers-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sung Kim
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
bashers-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.4
2
+ Name: bashers
3
+ Version: 0.1.0
4
+ Summary: Installable bash command helpers
5
+ Author: Sung Kim
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+
14
+ # Bashers
15
+ Installable bash command helpers
16
+
17
+ ## Installation
18
+
19
+ Install the package:
20
+
21
+ ```bash
22
+ pip install .
23
+ ```
24
+
25
+ Or with uv:
26
+
27
+ ```bash
28
+ uv pip install .
29
+ ```
30
+
31
+ Or install from a built wheel:
32
+
33
+ ```bash
34
+ pip install dist/bashers-*.whl
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ After installation, use the `bashers` dispatcher:
40
+
41
+ ```bash
42
+ bashers update
43
+ bashers update requests
44
+ bashers show
45
+ bashers show requests
46
+ bashers setup
47
+ bashers setup --frozen
48
+ ```
49
+
50
+ Verify the command is on PATH:
51
+
52
+ ```bash
53
+ which bashers
54
+ ```
55
+
56
+ If you are using a virtualenv or `uv`, make sure its `bin/` directory is active in your shell.
57
+
58
+ ## Development
59
+
60
+ To install in development mode:
61
+
62
+ ```bash
63
+ uv sync
64
+ ```
65
+
66
+ Or with pip:
67
+
68
+ ```bash
69
+ pip install -e .
70
+ ```
71
+
72
+ ## Adding New Commands
73
+
74
+ 1. Add your bash script under the `bashers/` directory (subfolders are OK)
75
+ 2. If you want a bash function, define a function with the same name as the file
76
+ 3. Reinstall: `uv sync` or `pip install -e .`
@@ -0,0 +1,63 @@
1
+ # Bashers
2
+ Installable bash command helpers
3
+
4
+ ## Installation
5
+
6
+ Install the package:
7
+
8
+ ```bash
9
+ pip install .
10
+ ```
11
+
12
+ Or with uv:
13
+
14
+ ```bash
15
+ uv pip install .
16
+ ```
17
+
18
+ Or install from a built wheel:
19
+
20
+ ```bash
21
+ pip install dist/bashers-*.whl
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ After installation, use the `bashers` dispatcher:
27
+
28
+ ```bash
29
+ bashers update
30
+ bashers update requests
31
+ bashers show
32
+ bashers show requests
33
+ bashers setup
34
+ bashers setup --frozen
35
+ ```
36
+
37
+ Verify the command is on PATH:
38
+
39
+ ```bash
40
+ which bashers
41
+ ```
42
+
43
+ If you are using a virtualenv or `uv`, make sure its `bin/` directory is active in your shell.
44
+
45
+ ## Development
46
+
47
+ To install in development mode:
48
+
49
+ ```bash
50
+ uv sync
51
+ ```
52
+
53
+ Or with pip:
54
+
55
+ ```bash
56
+ pip install -e .
57
+ ```
58
+
59
+ ## Adding New Commands
60
+
61
+ 1. Add your bash script under the `bashers/` directory (subfolders are OK)
62
+ 2. If you want a bash function, define a function with the same name as the file
63
+ 3. Reinstall: `uv sync` or `pip install -e .`
@@ -0,0 +1,65 @@
1
+ import importlib.resources
2
+ import subprocess
3
+ import sys
4
+ from pathlib import Path
5
+
6
+
7
+ def _create_script_runner(script_name: str):
8
+ def runner() -> int:
9
+ try:
10
+ with importlib.resources.path("bashers", script_name) as script_path:
11
+ if script_path.read_text().strip().startswith("#!"):
12
+ content = script_path.read_text()
13
+ if f"{script_name}()" in content:
14
+ result = subprocess.run(
15
+ [
16
+ "bash",
17
+ "-c",
18
+ f'source {script_path} && {script_name} "$@"',
19
+ "--",
20
+ ]
21
+ + sys.argv[1:],
22
+ )
23
+ else:
24
+ result = subprocess.run([str(script_path)] + sys.argv[1:])
25
+ return result.returncode
26
+ except Exception:
27
+ script_path = Path(__file__).parent / script_name
28
+ if script_path.exists():
29
+ content = script_path.read_text()
30
+ if f"{script_name}()" in content:
31
+ result = subprocess.run(
32
+ [
33
+ "bash",
34
+ "-c",
35
+ f'source {script_path} && {script_name} "$@"',
36
+ "--",
37
+ ]
38
+ + sys.argv[1:],
39
+ )
40
+ else:
41
+ result = subprocess.run([str(script_path)] + sys.argv[1:])
42
+ return result.returncode
43
+ return 1
44
+
45
+ return runner
46
+
47
+
48
+ def _discover_scripts():
49
+ scripts_dir = Path(__file__).parent
50
+ scripts = {}
51
+ for script_file in scripts_dir.glob("*"):
52
+ if (
53
+ script_file.is_file()
54
+ and not script_file.name.endswith((".py", ".pyc"))
55
+ and not script_file.name.startswith("__")
56
+ ):
57
+ script_name = script_file.name
58
+ if script_file.read_text().strip().startswith("#!"):
59
+ scripts[script_name] = _create_script_runner(script_name)
60
+ return scripts
61
+
62
+
63
+ _scripts = _discover_scripts()
64
+ for name, runner in _scripts.items():
65
+ globals()[name] = runner
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env bash
2
+
3
+ print_help() {
4
+ local pkg_dir="$1"
5
+ echo "Bashers - Bash command helpers"
6
+ echo "Usage: bashers <command> [args...]"
7
+ echo ""
8
+ echo "Available commands:"
9
+ if [[ -d "$pkg_dir" ]]; then
10
+ find "$pkg_dir" -type f \
11
+ | sed "s|^$pkg_dir/||" \
12
+ | grep -vE '(^__|/__)' \
13
+ | grep -vE '(^\.|/\.)' \
14
+ | grep -vE '\.(py|pyc)$' \
15
+ | sort \
16
+ | sed 's/^/ /'
17
+ fi
18
+ }
19
+
20
+ if [[ $# -eq 0 ]]; then
21
+ pkg_dir="$(
22
+ python - <<'PY'
23
+ import importlib.resources as resources
24
+ print(resources.files("bashers"))
25
+ PY
26
+ )"
27
+ print_help "$pkg_dir"
28
+ exit 0
29
+ fi
30
+
31
+ command="$1"
32
+ shift
33
+
34
+ case "$command" in
35
+ help|-h|--help)
36
+ pkg_dir="$(
37
+ python - <<'PY'
38
+ import importlib.resources as resources
39
+ print(resources.files("bashers"))
40
+ PY
41
+ )"
42
+ print_help "$pkg_dir"
43
+ exit 0
44
+ ;;
45
+ esac
46
+
47
+ pkg_dir="$(
48
+ python - <<'PY'
49
+ import importlib.resources as resources
50
+ print(resources.files("bashers"))
51
+ PY
52
+ )"
53
+
54
+ script_path=""
55
+ if [[ "$command" == */* ]]; then
56
+ candidate="$pkg_dir/$command"
57
+ [[ -f "$candidate" ]] && script_path="$candidate"
58
+ else
59
+ mapfile -t matches < <(find "$pkg_dir" -type f -name "$command" \
60
+ | grep -vE '(^__|/__)' \
61
+ | grep -vE '(^\.|/\.)' \
62
+ | grep -vE '\.(py|pyc)$')
63
+ if [[ ${#matches[@]} -gt 1 ]]; then
64
+ echo "Command '$command' is ambiguous. Use a path:" >&2
65
+ for match in "${matches[@]}"; do
66
+ echo " ${match#"$pkg_dir"/}" >&2
67
+ done
68
+ exit 2
69
+ fi
70
+ [[ ${#matches[@]} -eq 1 ]] && script_path="${matches[0]}"
71
+ fi
72
+
73
+ if [[ -n "$script_path" && -f "$script_path" ]]; then
74
+ script_name="$(basename "$script_path")"
75
+ if grep -qE "^[[:space:]]*${script_name}[[:space:]]*\\(\\)[[:space:]]*\\{" "$script_path"; then
76
+ exec bash -c "source \"$script_path\" && $script_name \"\$@\"" -- "$@"
77
+ fi
78
+ exec "$script_path" "$@"
79
+ fi
80
+
81
+ echo "Command '$command' not found. Use 'bashers help' for available commands."
82
+ exit 1
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bash
2
+
3
+ setup() {
4
+ local arg="${1-}" want_frozen=0 want_no_cache=0
5
+ if [[ "$arg" == "-h" || "$arg" == "--help" ]]; then
6
+ cat <<'EOF'
7
+ Usage: setup [--rm] [--frozen|--locked|--sync]
8
+
9
+ Install project dependencies using uv or poetry.
10
+
11
+ Options:
12
+ --rm Remove .venv before install (implies --no-cache where supported)
13
+ --frozen Use a locked/frozen install if supported
14
+ --locked Same as --frozen
15
+ --sync Same as --frozen
16
+ EOF
17
+ return 0
18
+ fi
19
+ [[ "$arg" == "--rm" ]] && rm -rf .venv && want_no_cache=1 && shift && arg="${1-}"
20
+ [[ "$arg" == "--frozen" || "$arg" == "--locked" || "$arg" == "--sync" ]] && want_frozen=1
21
+
22
+ _need() { command -v "$1" >/dev/null 2>&1 || { echo "[setup]: $2" >&2; return 127; }; }
23
+ _has() { [[ -f "$1" ]] || return 1; }
24
+ _tbl() { [[ -f pyproject.toml ]] && grep -Eq "^\[$1\]" pyproject.toml; }
25
+
26
+ if _has uv.lock || _tbl project; then
27
+ _need uv "uv required but not on PATH" || return $?
28
+ local uv_opts="--all-extras"
29
+ (( want_frozen )) && uv_opts="$uv_opts --frozen"
30
+ (( want_no_cache )) && uv_opts="$uv_opts --no-cache"
31
+ uv sync $uv_opts
32
+ return $?
33
+ fi
34
+
35
+ if _has poetry.lock || _tbl tool.poetry; then
36
+ _need poetry "poetry required but not on PATH" || return $?
37
+ local poetry_opts="--all-extras"
38
+ [[ "$arg" == "--frozen" || "$arg" == "--locked" || "$arg" == "--sync" ]] && poetry_opts="$poetry_opts --sync"
39
+ (( want_no_cache )) && poetry_opts="$poetry_opts --no-cache"
40
+ poetry install $poetry_opts
41
+ return $?
42
+ fi
43
+
44
+ echo "[setup]: no uv/poetry lockfile and no pyproject.toml with [project]/[tool.poetry] in $(pwd)" >&2
45
+ return 2
46
+ }
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env bash
2
+
3
+ show() {
4
+ if [[ "${1-}" == "-h" || "${1-}" == "--help" ]]; then
5
+ cat <<'EOF'
6
+ Usage: show [pattern...]
7
+
8
+ List installed Python packages for the current project.
9
+
10
+ With patterns, filters results (case-insensitive).
11
+ Works with uv or poetry projects.
12
+ EOF
13
+ return 0
14
+ fi
15
+ _need() { command -v "$1" >/dev/null 2>&1 || { echo "[show]: $1 not on PATH" >&2; return 127; }; }
16
+ _has_project() { [[ -f pyproject.toml ]] && grep -qE '^\[project\]' pyproject.toml; }
17
+ _has_poetry() { [[ -f pyproject.toml ]] && grep -qE '^\[tool\.poetry\]' pyproject.toml; }
18
+
19
+ if [[ -f uv.lock ]] || _has_project; then
20
+ _need uv || return $?
21
+ if [[ $# -eq 0 ]]; then
22
+ uv pip list
23
+ else
24
+ uv pip list | grep -iE "$(IFS='|'; echo "$*")"
25
+ fi
26
+ return $?
27
+ fi
28
+
29
+ if [[ -f poetry.lock ]] || _has_poetry; then
30
+ _need poetry || return $?
31
+ if [[ $# -eq 0 ]]; then
32
+ poetry show
33
+ else
34
+ poetry show | grep -iE "$(IFS='|'; echo "$*")"
35
+ fi
36
+ return $?
37
+ fi
38
+
39
+ echo "[show]: no uv/poetry project found in $(pwd)" >&2
40
+ return 2
41
+ }
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+
3
+ update() {
4
+ local pkg="${1-}"
5
+
6
+ if [[ "$pkg" == "-h" || "$pkg" == "--help" ]]; then
7
+ cat <<'EOF'
8
+ Usage: update [package]
9
+
10
+ Update Python dependencies in the current project.
11
+
12
+ If [package] is provided, updates just that package (with fuzzy matching).
13
+ Works with uv or poetry projects.
14
+ EOF
15
+ return 0
16
+ fi
17
+
18
+ [[ -n "$pkg" && "$pkg" == -* ]] && { echo "[update]: usage: update [package]" >&2; return 2; }
19
+ _need() { command -v "$1" >/dev/null 2>&1 || { echo "[update]: $1 not on PATH" >&2; return 127; }; }
20
+ _has_project() { [[ -f pyproject.toml ]] && grep -qE '^\[project\]' pyproject.toml; }
21
+ _has_poetry() { [[ -f pyproject.toml ]] && grep -qE '^\[tool\.poetry\]' pyproject.toml; }
22
+ _has_prerelease() {
23
+ [[ -f pyproject.toml ]] && grep -qE '[0-9]+\.[0-9]+(\.[0-9]+)?[a-zA-Z]|\.(a|b|rc|dev|pre|alpha|beta)[0-9]' pyproject.toml
24
+ }
25
+
26
+ local packages_cmd=""
27
+ if [[ -f uv.lock ]] || _has_project; then
28
+ _need uv || return $?
29
+ packages_cmd="uv pip list 2>/dev/null | tail -n +3 | awk '{print \$1}'"
30
+ elif [[ -f poetry.lock ]] || _has_poetry; then
31
+ _need poetry || return $?
32
+ packages_cmd="poetry show 2>/dev/null | grep -E '^name\s+:' | sed 's/^name\s*:\s*//'"
33
+ else
34
+ echo "[update]: no uv/poetry project found in $(pwd)" >&2
35
+ return 2
36
+ fi
37
+
38
+ if [[ -n "$pkg" ]]; then
39
+ local matches=$(eval "$packages_cmd" | grep -iE "$pkg")
40
+ local count=$(echo "$matches" | grep -c . || echo "0")
41
+ [[ "$count" -eq 0 ]] && { echo "[update]: no packages found matching '$pkg'" >&2; return 1; }
42
+ [[ "$count" -gt 1 ]] && { _need fzf || return 127; pkg=$(echo "$matches" | fzf --prompt="Package> "); }
43
+ [[ -z "$pkg" ]] && return 1
44
+ fi
45
+
46
+ if [[ -f uv.lock ]] || _has_project; then
47
+ local prerelease_flag=""
48
+ _has_prerelease && prerelease_flag="--prerelease allow"
49
+ [[ -z "$pkg" ]] && uv lock --upgrade $prerelease_flag || uv lock --upgrade-package "$pkg" $prerelease_flag
50
+ uv sync --all-extras
51
+ else
52
+ local prerelease_flag=""
53
+ _has_prerelease && prerelease_flag="--allow-prereleases"
54
+ [[ -z "$pkg" ]] && poetry update $prerelease_flag || poetry update "$pkg" $prerelease_flag
55
+ fi
56
+ return $?
57
+ }
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.24.1"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "bashers"
7
+ version = "0.1.0"
8
+ description = "Installable bash command helpers"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "Sung Kim" }]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ ]
18
+
19
+
20
+ [tool.hatch.build.targets.wheel]
21
+ packages = ["bashers"]
22
+ include = ["bashers/*"]
23
+ shared-scripts = { "bashers/bashers" = "bashers" }
24
+ exclude = ["bashers/__pycache__/*"]
25
+
26
+ [tool.hatch.build.targets.sdist]
27
+ include = ["bashers/*", "README.md", "pyproject.toml"]
28
+ exclude = ["bashers/__pycache__/*"]
29
+
30
+ [tool.semantic_release]
31
+ branch = "main"
32
+ version_toml = ["pyproject.toml:project.version"]