Pynosaur

Modern CLI tools with better UX

View the Project on GitHub Pynosaur/pynosaur

← Back

pget

Pure Python package manager for the pynosaur ecosystem

pget is a minimalist package manager for lightweight Python CLI tools. It installs standalone executables to ~/.pget/bin, making it easy to distribute and install simple command-line utilities without pip or virtual environments.

pget install yday

Table of Contents

What is pget?

pget is a package manager designed specifically for standalone Python CLI applications in the pynosaur ecosystem. Unlike pip which manages libraries and dependencies, pget focuses on distributing self-contained executable tools.

Why pget?

Use Cases:

Installation

Option 1: Clone and Run (Quick Start)

No build required - start using pget immediately:

git clone https://github.com/pynosaur/pget.git
cd pget
python app/main.py --help

With this option, use python app/main.py for all commands.

Build once, then use the pget command anywhere. Requires Bazel or Bazelisk:

git clone https://github.com/pynosaur/pget.git
cd pget
bazel build //:pget_bin
mkdir -p ~/.pget/bin
cp bazel-bin/pget ~/.pget/bin/
export PATH="$HOME/.pget/bin:$PATH"  # Add to your shell rc file

Now you can use pget directly from anywhere (instead of python app/main.py).

Note: The ~/.pget/bin directory is where pget installs all apps, including itself. Make sure this directory is in your PATH.

Quick Start

If running from source, use python app/main.py. If you built the standalone binary, use pget directly.

# Search for available packages
pget search

# Install an app (e.g., yday - prints current day of year)
pget install yday

# List installed packages
pget list

# Use the installed app
yday
# Output: 360

# Update an app
pget update yday

# Remove an app
pget remove yday

Note: If running from source without building, replace pget with python app/main.py in all commands above.

Features

Commands

Note: The examples below use pget (standalone binary). If running from source, use python app/main.py instead.

install

Install a package from the pynosaur organization:

pget install <app_name>

Example output:

Installing yday
Looking for binary: yday-darwin-arm64
Downloading yday (2.1 MB)
Installing yday to /Users/username/.pget/bin/yday
yday installed successfully

remove

Uninstall a previously installed package:

pget remove <app_name>

list

Show all installed packages:

pget list

Example output:

Installed packages in /Users/username/.pget/bin:

  pget                 0.1.0
  yday                 0.1.0

update

Update a package to the latest version:

pget update <app_name>

Search for available packages in the pynosaur organization:

# List all packages
pget search

# Search with a query
pget search date

Example output:

Name                 Description
yday                 Prints the current day of the year (1-366)
pget                 Pure Python package manager for pynosaur ecosystem

Global Options

Example Apps

Apps currently available in the pynosaur ecosystem:

To see all available apps, run:

pget search

Requirements

Platform Support:

License

MIT License - See LICENSE file for details.


App Development Guidelines

This section is for developers who want to create apps compatible with pget. Following these guidelines ensures your app can be easily installed and distributed through the pynosaur ecosystem.

Repository Requirements

  1. GitHub Organization: Apps must be in the pynosaur GitHub organization
  2. Repository Name: Should match the app name (e.g., yday, pget)
  3. Standard Structure: Must follow the directory structure:
    <app_name>/
    ├── app/
    │   ├── __init__.py
    │   └── main.py          # Main entry point
    ├── doc/
    │   └── <app_name>.yaml  # App metadata
    ├── test/
    │   └── test_*.py        # Test files
    ├── BUILD                # Bazel build file (required for source builds)
    ├── MODULE.bazel         # Bazel module file (required for source builds)
    └── README.md            # Documentation
    

Code Requirements

Main Entry Point (app/main.py)

Version Information

Example Structure

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
from app import __version__

def main():
    args = sys.argv[1:]
    
    if not args:
        # Default behavior
        return 0
    
    if args[0] in ("-h", "--help"):
        print("Usage: app_name [options]")
        return 0
    
    if args[0] in ("-v", "--version"):
        print(__version__)
        return 0
    
    # Your app logic here
    return 0

if __name__ == "__main__":
    sys.exit(main())

Build System (Bazel)

Required Files

  1. MODULE.bazel: Bazel module configuration
    module(
        name = "<app_name>",
        version = "0.1.0",
    )
       
    bazel_dep(name = "rules_python", version = "0.40.0")
       
    python = use_extension("@rules_python//python/extensions:python.bzl", "python")
    python.toolchain(python_version = "3.11")
    
  2. BUILD: Bazel build rules
    genrule(
        name = "<app_name>_bin",
        srcs = glob(["app/**/*.py"]),
        outs = ["<app_name>"],
        cmd = """
            /opt/homebrew/bin/nuitka \
                --onefile \
                --onefile-tempdir-spec=/tmp/nuitka-<app_name> \
                --no-progressbar \
                --assume-yes-for-downloads \
                --output-dir=$$(dirname $(location <app_name>)) \
                --output-filename=<app_name> \
                $(location app/main.py)
        """,
        local = 1,
        visibility = ["//visibility:public"],
    )
    

Build Target

Binary Distribution

Release Binaries

For faster installation, provide pre-compiled binaries in GitHub releases:

  1. Naming Convention: {app_name}-{platform}
    • Examples: yday-darwin-arm64, yday-linux-x86_64, yday-windows-x86_64
  2. Supported Platforms:
    • darwin-arm64 (Apple Silicon macOS)
    • darwin-x86_64 (Intel macOS)
    • linux-x86_64 (Linux x86_64)
    • linux-arm64 (Linux ARM64)
    • windows-x86_64 (Windows x86_64)
  3. Release Process:
    • Create a GitHub release with tag (e.g., v0.1.0)
    • Upload binary assets with the correct naming convention
    • Upload a source tarball (e.g., <app_name>-source.tar.gz)

Source Build Fallback

If no release binary is available, pget will:

  1. Download the source code from the repository
  2. Look for MODULE.bazel or BUILD file
  3. Build using Bazel with the target //:{app_name}_bin
  4. Install the resulting binary

Note: Users need Bazel (or bazelisk) installed for source builds.

Documentation

README.md

Should include:

doc/{app_name}.yaml (Optional)

Metadata file for app information (ALL CAPS field names):

NAME: <app_name>
VERSION: "0.1.0"
DESCRIPTION: >
  Brief description of the app
USAGE:
  - "<app_name>"
  - "<app_name> --help"
  - "<app_name> --version"
OPTIONS:
  - "-h, --help        Show help message"
  - "-v, --version     Show version information"
OUTPUT: Description of output
AUTHOR: "@username"
DATE: "YYYY-MM-DD"
NOTES: []

Testing

Program Types

pget is designed for CLI (Command Line Interface) tools:

Not suitable for:

Installation Flow

When a user runs pget install <app_name>:

  1. Check Repository: Verifies app exists in pynosaur organization
  2. Try Binary Download: Looks for release binary matching user’s platform
  3. Fallback to Source: If no binary, downloads source and builds with Bazel
  4. Install Binary: Copies binary to ~/.pget/bin/
  5. Install Documentation: Copies doc/ to ~/.pget/helpers/<app_name>/doc/
  6. Create Metadata: Saves install info to ~/.pget/helpers/<app_name>/.pget-metadata.json
  7. Make Executable: Sets executable permissions
  8. Update PATH: Ensures ~/.pget/bin is in user’s PATH

Data Storage and Directory Structure

Apps installed by pget use a hybrid directory structure:

~/.pget/
├── bin/                    # All executables (in PATH)
│   └── <app_name>
└── helpers/                # Per-app helper files and data
    └── <app_name>/
        ├── .pget-metadata.json  # Install metadata (created by pget)
        ├── doc/                 # Documentation (created by pget)
        │   └── <app_name>.yaml
        ├── data/                # Optional: create if needed
        ├── config/              # Optional: create if needed
        └── cache/               # Optional: create if needed

Directory Purposes

Using Data Directories in Your App

Apps that need persistent storage should use the standard directories:

from pathlib import Path

APP_NAME = "myapp"
PGET_HELPERS = Path.home() / ".pget" / "helpers"

# App directories
APP_ROOT = PGET_HELPERS / APP_NAME
DATA_DIR = APP_ROOT / "data"
CONFIG_DIR = APP_ROOT / "config"
CACHE_DIR = APP_ROOT / "cache"

def ensure_dirs():
    """Create app directories as needed."""
    DATA_DIR.mkdir(parents=True, exist_ok=True)
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)

def save_database():
    ensure_dirs()
    db_path = DATA_DIR / "database.json"
    # ... save to db_path

Important: Apps must create data/, config/, and cache/ directories themselves when needed. Only doc/ is created by pget during installation.

Best Practices

  1. Keep it simple: Focus on doing one thing well
  2. Pure Python: Prefer standard library, minimize external dependencies
  3. Error handling: Provide clear error messages
  4. Documentation: Include helpful --help output
  5. Testing: Write tests for core functionality
  6. Versioning: Use semantic versioning (e.g., 0.1.0)
  7. Releases: Tag releases and provide binaries for common platforms

Quick Start for App Developers

Creating a new pget-compatible app:

  1. Create repository structure
    mkdir myapp
    cd myapp
    mkdir -p app doc test
    
  2. Create app/__init__.py
    __version__ = "0.1.0"
    
  3. Create app/main.py
    #!/usr/bin/env python3
    import sys
    from app import __version__
       
    def main():
        args = sys.argv[1:]
        if args and args[0] in ("-h", "--help"):
            print("Usage: myapp [options]")
            return 0
        if args and args[0] in ("-v", "--version"):
            print(__version__)
            return 0
           
        # Your app logic here
        print("Hello, Friend?")
        return 0
       
    if __name__ == "__main__":
        sys.exit(main())
    
  4. Create MODULE.bazel (copy from yday example)

  5. Create BUILD (copy from yday example, update app name)

  6. Test locally
    python app/main.py
    
  7. Build with Bazel
    bazel build //:myapp_bin
    
  8. Create GitHub release with binary assets

For a complete working example, see the yday repository.


Contributing

Ahoy there! Code, code:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests if applicable
  5. Submit a pull request

Support

Project Status

pget is in active development. The core functionality is stable and ready for use, but APIs may change as the project evolves.