Live data from Hacker News

Using Python for Scripting

hypirion.com

61–70 of 112 posts

Re: Using Python for Scripting

#61
I like the message the article is trying to convey, Python is good alternative to complicated shell scripts in my opinion.

I do wonder, let's say the scripting file is using lots of libraries, do you have to include some kind of requirements.txt file with it aswell when you want to share it with other people?

In Ruby, there is inline bundler which makes sharing a single Ruby script very portable.

https://bundler.io/guides/bundler_in_a_single_file_ruby_scri...

Re: Using Python for Scripting

#62
post #17

Odd, I don't see any mention of subprocess.run, the workhorse of python scripting. Quick rundown for the unfamiliar: Give it a command as a list of strings (e.g., subprocess.run(["echo", "foo"]).) It takes a bunch of flags, but the most useful (but not immediately obvious) ones are: check=True: Raise an error if the command fails capture_output=True: Captures stdout/stderr on the CompletedProcess text=True: Automatic…

One thing I can recommend that makes scripting in python with external commands a lot easier is the `sh` module: https://pypi.org/project/sh/ Basically you can just `from sh import [command]` and then have an installed binary command available as function from sh import ifconfig print(ifconfig("eth0"))

I've used Plumbum for this for some projects at work, and really like it for this.

https://plumbum.readthedocs.io/en/latest/local_commands.html...

It also does argument parsing and validation, so it's generally pretty useful for writing little CLI tools that invoke other CLI tools.

https://plumbum.readthedocs.io/en/latest/cli.html

Re: Using Python for Scripting

#63
post #24
post #10

Earlier quoted context omitted.

This works really well in my experience, but it does mean you need to have a working internet connection the first time you run the script. # /// script # dependencies = [ # "cowsay", # ] # /// import cowsay cowsay.cow("Hello World") Then: uv run cowscript.py It manages a disposable hidden virtual environment automatically, via a very fast symlink-based caching mechanism. You can also add a shebang line so you can ex…

I wish env -S was more portable. It's a newer feature of the coreutils env implementation and isn't supported elsewhere afaik.

You can use so-called "exec magic" instead of `env -S`. Here is an explanation with Python examples: https://dbohdan.com/scripts-with-dependencies#exec-magic (disclosure: my site). In short:

  #! /bin/sh
  "exec" "/usr/bin/env" "uv" "run" "--quiet" "--script" "$0" "$@"
  # /// script
  # dependencies = [
  #   "cowsay",
  # ]
  # ///
  import cowsay
  cowsay.cow("Hello, world!")
On systems that can't run uv, like NetBSD and OpenBSD, switch to pipx:

  #! /bin/sh
  "exec" "/usr/bin/env" "pipx" "run" "$0" "$@"
  # ...

Re: Using Python for Scripting

#64

If a script is simple - I use posix sh + awk, sed, etc. But if a script I write needs to use arrays, sets, hashtable or processes many files - I use Nim[0]. It's a compiled systems-programming language that feels like a scripting language: - Nim is easy to write and reads almost like a pseudocode. - Nim is very portable language, runs almost anywhere C can run (both compiler and programs). - `nim r script.nim` to com…

> If a script is simple - I use posix sh + awk, sed, etc.

> But if a script I write needs to use arrays, sets, hashtable or processes many files

One option that I sometimes use at work (in addition to writing some Python CLIs) that is a pretty nice next step on this spectrum is Bash-with-all-the-fixins'.

I use a Nix-based dependency resolver for shell scripts called resholve¹ to parse scripts so that it can identify all of their dependencies (including Bash itself), then produce a "compiled" script as a Nix build where all of the references to external programs are replaced with pinned Nix store paths.

Then I have a fixed (and recent) version of GNU Bash regardless of platform, so I'm free to use Bash features that are newer or nicer than POSIX sh. My favorite such features are `mapfile` and `lastpipe` for writing in a more functional style, plus of course maps ("associative arrays").

I don't have to worry about portability problems with common utilities, because my scripts will bring along the expected implementations of coreutils, `find`, `grep`, etc. I'm free to use "modern" alternatives to classic utilities (like `rg` instead of GNU grep or `fd` instead of GNU findutils) if they offer better performance or more readable syntax. Polished interactivity is easy to build by just embedding a copy of `fzf`. And while I don't love Bash for working with structured data, it becomes a lot less painful when I can just pull in `jq`.

It's obviously got some disadvantages versus an option like Python, but the amount-of-code to functionality ratio is also much more favorable than Python's.

I typically use this for scripting build and development tasks in projects that already use Nix (via Devenv— I have some unpublished changes to the scripts module that add resholve and ShellCheck integration) to manage their environments, so there's no special packaging/distribution/setup burden.

IME it makes for vastly more readable and maintainable shell scripts than painstakingly limiting oneself to pure POSIX sh, so it can serve quite well as a middle ground option between barebones sh and a "real" programming language as your little script gradually grows in complexity.

> if you need external deps - just statically link them and distribute a cross-compiled binary (use zigcc[1] or easy Nim cross-compilation).

The deployment story sounds very slick and hard to beat! This is something I might want to try for scripts I want to be easy to distribute on macOS systems that don't have Nix installed.

--

1: https://github.com/abathur/resholve

Re: Using Python for Scripting

#65
post #56

Earlier quoted context omitted.

It’s like Vim, you learn it once, and you keep using it forever once you’re used to it. I’m so thankful to see a flake.nix file in every single cool project on code forges.

Yea that's a common theme of excuses for both Rust and Nix. Wrong though, because most anyone who can use a computer at all can learn the basics of Vim. Seeing that flake.nix badge of complexity lets me know a project will be a nightmare to set up and will break every other week. It's usually right next to the Cargo.toml badge with 400 dependencies underneath.

Nix with Flakes never randomly break, I still have projects from 3 or 4 years ago that I can still run `nix build` and getting it running. Yes, if you try to update the `flake.lock` this may introduce breakages, but this is expected if you're pining `nixos-unstable` instead of a stable branch.

Re: Using Python for Scripting

#66
I wrote another comment here about a strategy for writing portable Bash scripts without compromising on features and freely using arbitrary external commands. I wanted to give an example from the article of how I'd likely write one of his examples of a "somewhat unreadable shell script" in this style.

His ugly sh example:

  morning_greetings=('hi' 'hello' 'good morning')
  energetic_morning_greetings=()

  for s in "${morning_greetings[@]}"; do
    energetic_morning_greetings+=( "${s^^}!" )
  done
and his more readable Python equivalent:

  morning_greetings = ['hi', 'hello', 'good morning']
  energetic_morning_greetings = \
     [s.upper() + '!' for s in morning_greetings]

And I'd write the shell version in Bash something like this:

  morning_greetings=(hi hello 'good morning')
  printf '%s!\n' "${morning_greetings[@]}" \
    | tr '[:lower:]' '[:upper:]' \
    | readarray -t energetic_morning_greetings
Does it still involve more syntax? Yeah. Printing arrays in Bash always involves some. But it's clearer at a glance what it does, and it doesn't involve mutating variables.

The piece this example is too simple to show (since it's focused only on data operations and not interacting with the filesystem or running external programs) is how much shorter the Bash usually ends up being than the Python equivalent.

Re: Using Python for Scripting

#67
post #17

Odd, I don't see any mention of subprocess.run, the workhorse of python scripting. Quick rundown for the unfamiliar: Give it a command as a list of strings (e.g., subprocess.run(["echo", "foo"]).) It takes a bunch of flags, but the most useful (but not immediately obvious) ones are: check=True: Raise an error if the command fails capture_output=True: Captures stdout/stderr on the CompletedProcess text=True: Automatic…

One thing I can recommend that makes scripting in python with external commands a lot easier is the `sh` module: https://pypi.org/project/sh/ Basically you can just `from sh import [command]` and then have an installed binary command available as function from sh import ifconfig print(ifconfig("eth0"))

`sh` is nice but it requires a dependency. No dependencies is nicer IMHO. uv makes this way easier but for low dependency systems, or unknown environments stdlib is king.

Re: Using Python for Scripting

#68

Earlier quoted context omitted.

One thing I can recommend that makes scripting in python with external commands a lot easier is the `sh` module: https://pypi.org/project/sh/ Basically you can just `from sh import [command]` and then have an installed binary command available as function from sh import ifconfig print(ifconfig("eth0"))

uv for using sh as a dependency in scripts, managed inline, has changed it from “eh, I’ll just use subprocess” to “why not” for me. https://docs.astral.sh/uv/guides/scripts/#using-different-py...

I love uv, the why not works great if it’s your machine but in places without your machine uv is just another step for a customer.

Re: Using Python for Scripting

#69

The Python stdlib does not get enough credit. People complain about things like how its http client is dated and slow, but it’s pretty amazing that it’s just right there if you need it, no external dependencies needed. And it’s sitting right next to difflib, graphlib, pathlib, struct, glob, tkinter, and dozens of others. Sure, every one of these is limited individually, but those limitations are stable and well under…

Absolutely agree, but it's funny that you mentioned graphlib. It has a single algorithm (topological sort)!

The sqlite, tkinter, and shelve modules are the ones I find most impressive.

Post reply on HN