Skip to main content

A port of Go's flag package to Python

Project description

go-flag

go-flag is a port of Go's flag package to Python.

Why??

Typically, click or argparse are going to be more straightforward than using this library. But there are a few motivations for using go-flag:

  1. You want to write a tool in Python, which behaves like a Go program. If you are using it alongside other programs that use Go-style flags, it can make your tool feel more at home in that ecosystem.
  2. You're a Gopher, and want to write some Python. In that case, this library may feel more comfortable.
  3. You are porting a Go program. This library can help minimize the amount of effort involved in translating idioms.

Also, I think this is funny.

Usage

The simplest usage of this library involves defining some flags and running a parse:

#!/usr/bin/env python

import flag

force = flag.bool_("force", False, "force the command to execute")
count = flag.int_("count", 1, "a count")
name = flag.string("name", "Josh", "a name")
threshold = flag.float_("threshold", 1.0, "a threshold")

flag.parse()

print(dict(
    force=force.deref(),
    count=count.deref(),
    name=name.deref(),
    threshold=threshold.deref()
))

With no arguments, this will print:

$ python examples/simple.py
{'force': False, 'count': 1, 'name': 'Josh', 'threshold': 1.0}

With a number of argument, we see:

$ python examples/simple.py -count 3 -force=true -name KB -threshold 0.5
{'force': True, 'count': 3, 'name': 'KB', 'threshold': 0.5}

With the help flag, this will print:

$ python examples/simple.py -h
Usage of examples/simple.py:

  -count int
    	a count (default 1)
  -force
    	force the command to execute
  -name string
    	a name (default Josh)
  -threshold float
    	a threshold (default 1)

In this usage, these flags are instances of flag.Ptr. But you may want to be a little more fancy - for instance, using a class and flag.AttrRef:

#!/usr/bin/env python

import flag


class Config:
    force: bool = flag.zero.bool_
    count: int = flag.zero.int_
    name: str = flag.zero.string
    threshold: float = flag.zero.float_


force = flag.AttrRef(Config, "force")
count = flag.AttrRef(Config, "count")
name = flag.AttrRef(Config, "name")
threshold = flag.AttrRef(Config, "threshold")

flag.bool_var(force, "force", False, "force the command to execute")
flag.int_var(count, "count", 1, "a count")
flag.string_var(name, "name", "Josh", "a name")
flag.float_var(threshold, "threshold", 1.0, "a threshold")

flag.parse()

print(
    dict(
        force=Config.force,
        count=Config.count,
        name=Config.name,
        threshold=Config.threshold,
    )
)

This outputs:

$ python examples/class.py -count 3 -force=true -name KB -threshold 0.5
{'force': True, 'count': 3, 'name': 'KB', 'threshold': 0.5}

The flag.KeyRef class can implement a similar pattern with dicts.

In general, aside from the need to use classes that fake pointers and a number of data types not applicable to Python, the API should follow the same general shape as Go's flag package. For more documentation, read the source - the docstrings should be relatively complete.

Error Handling

We already saw one strange set of abstractions we needed to pretend to be Go - the Pointer protocol and its implementations. The other way in which this library emulates Go is in its error handling.

Not to worry - this library raises Exceptions like God intended. But it does have two non-overlapping classes of errors: flag.Error and flag.Panic. The former emulates cases where Go would have us return an error. The latter is raised when emulating a Go panic.

While the internal details of how Errors are created are unusual, the end result is very simple error classes. In general, you can except on flag.Error and allow raised instances of flag.Panic to crash the program. But if you wish to have more fine-grained control, you may with to except flag.Panic as well.

Development

I developed this project using uv. It is a little immature, and I honestly can't recommend it yet for production use. We will see if I stick with this stack over time.

Nevertheless, the justfile should contain most of what you need - including just format, just lint, just check, and just test. Note that type checking requires node.js, because I use pyright.

License

I licensed this under a BSD-3 license, in an attempt to stay compatible with Go's license.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

go_flag-2.0.1.tar.gz (22.4 kB view details)

Uploaded Source

Built Distribution

go_flag-2.0.1-py3-none-any.whl (19.1 kB view details)

Uploaded Python 3

File details

Details for the file go_flag-2.0.1.tar.gz.

File metadata

  • Download URL: go_flag-2.0.1.tar.gz
  • Upload date:
  • Size: 22.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.5.2

File hashes

Hashes for go_flag-2.0.1.tar.gz
Algorithm Hash digest
SHA256 f829fc72785ac1f93573c10953a29926b5106a0ac73239b0177f4917891881b4
MD5 9ad09579baa200f48c98d2bfef914f84
BLAKE2b-256 58fe177da4854866e2eaa635d38b666041e4e53be1492c5930295dc63873dd3f

See more details on using hashes here.

File details

Details for the file go_flag-2.0.1-py3-none-any.whl.

File metadata

  • Download URL: go_flag-2.0.1-py3-none-any.whl
  • Upload date:
  • Size: 19.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.5.2

File hashes

Hashes for go_flag-2.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 cea4d6618775cb10da1beb08dc4a6822cdfca122b0d980aadd10405eb3573472
MD5 d7ed19a7b78a84be11c68ed1fcd8ff07
BLAKE2b-256 f4532e2a4bca6665bc4c5f5d9eec5e32ebeb8ee4a39fb98112e4263ea3049b15

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page