Skip to content

[WIP] Port switch-to-configuration script to Python#48378

Closed
bgamari wants to merge 5 commits intoNixOS:masterfrom
bgamari:nixos-cross-fixes
Closed

[WIP] Port switch-to-configuration script to Python#48378
bgamari wants to merge 5 commits intoNixOS:masterfrom
bgamari:nixos-cross-fixes

Conversation

@bgamari
Copy link
Contributor

@bgamari bgamari commented Oct 14, 2018

Motivation for this change

Note that this has yet to be tested. Rather I am just posting this as a placeholder.

This is a port of the switch-to-configuration script from Perl to Python. The motivation for this change is to ease cross-compilation, which is documented to be quite difficult in the case of Perl (see #36675).

One tricky issue that this raises is the lack of sensible dbus bindings for Python. Each of the options has its share of issues:

  • dbus-python is largely unmaintained and binds the libdbus implementation, use of which is somewhat actively discouraged
  • pydbus uses gobject-introspection, which sadly appears to be very unfriendly to cross-compilation
  • QtDBus uses Qt, which is rather heavy-weight for this purpose.

Ideally I would like a small binding for sdbus but, that not being available, I have simply used dbus-python here.

Things done
  • Tested using sandboxing (nix.useSandbox on NixOS, or option sandbox in nix.conf on non-NixOS)
  • Built on platform(s)
    • NixOS
    • macOS
    • other Linux distributions
  • Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nox --run "nox-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Determined the impact on package closure size (by running nix path-info -S before and after)
  • Fits CONTRIBUTING.md.

@bgamari
Copy link
Contributor Author

bgamari commented Oct 14, 2018

CCing @shlevy @Ericson2314

@GrahamcOfBorg GrahamcOfBorg added 6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (update) This PR changes an existing module in `nixos/` 10.rebuild-darwin: 501+ This PR causes many rebuilds on Darwin and should normally target the staging branches. 10.rebuild-linux: 501+ This PR causes many rebuilds on Linux and should normally target the staging branches. labels Oct 14, 2018
Copy link
Member

@FRidh FRidh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the main code flow into main() and add

if __name__ == "__main__": 
    main()

import statements should all be at the top. Please also add docstrings.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind to type annotations, then changing this file becomes easier for type checking with mypy.
I can also add them if you want.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was very tempted to add type annotations when I started but wasn't quite sure whether we would want to retain Python 2 support so (mostly) refrained. I would be happy to add them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we definitely don't want more legacy python2 code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider a function error which calls print with file=stderr
https://stackoverflow.com/questions/5574702/how-to-print-to-stderr-in-python

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use argparse for building the parser. Call it from main().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constants should be in uppercase

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check_call

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check_call

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use .format

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or python3.6 format strings

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we ready to accept a >= Python 3.6 as a requirement for NixOS?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. python36 is the default python3, and soon it will be python37.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

underscores

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what's below this for loop should be in a separate function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

too much nested. Split up in functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add those lines in a one main function instead of mixing functions and code?

@FRidh
Copy link
Member

FRidh commented Oct 14, 2018

@Mic92 I think we can add a tiny builder in trivial-builders for building a Python script, that is also being checked with mypy and pylint. We may want to have the option for disabling those checks.

@Mic92
Copy link
Member

Mic92 commented Oct 14, 2018

There is also pystemd: https://github.com/facebookincubator/pystemd that wraps around libsystemd,
but I don't know what the story of cross-compiling cython is at the moment. It should be lightweight at least.
cc @flokli #47517

@Mic92
Copy link
Member

Mic92 commented Oct 14, 2018

@Mic92
Copy link
Member

Mic92 commented Oct 14, 2018

I also started rewriting setup-etc.pl at some point, but I think I lost the source code for that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not valid python syntax.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also this one should be not.

@arianvp
Copy link
Member

arianvp commented Oct 14, 2018

Pystemd would indeed be very nice for this usecase. @flokli and I have been experimenting with it and it seemed to work really well. I'll have a look at Cython's cross-compilation story. Because I'm not sure either.

@jtojnar
Copy link
Member

jtojnar commented Oct 14, 2018

What about using Jeepney for DBus? It is a pure Python library.

@bgamari
Copy link
Contributor Author

bgamari commented Oct 14, 2018

What about using Jeepney for DBus? It is a pure Python library.

I considered it but decided against it for now for concerns of correctness and verbosity. I'd be happy to go this direction if others think it best, however.

@bgamari
Copy link
Contributor Author

bgamari commented Oct 14, 2018

The current head typechecks with mypy.

Copy link
Member

@Mic92 Mic92 Oct 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functions should also always have a return type also if it its None. Otherwise the type check is skipped if a function does not have any typed arguments.

Copy link
Member

@Mic92 Mic92 Oct 14, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def is_nixos() -> bool:

The build process relies on the glib-marshal utility.
Strangely, they were previously marked as nativeBuildInputs.
@bgamari
Copy link
Contributor Author

bgamari commented Oct 14, 2018

@Mic92, this now includes a port of setup-etc as well (although it might be wise to split this into a new PR)

@FRidh
Copy link
Member

FRidh commented Nov 1, 2018

FWIW, see #48611 regarding nfs-utils.

@bgamari
Copy link
Contributor Author

bgamari commented Nov 1, 2018

Alright, I will put this aside then. Thanks to everyone who reviewed!

@edolstra
Copy link
Member

edolstra commented Nov 1, 2018

The only Python script in NixOS is systemd-boot-builder.py, which kind of slipped in under the radar. I once started to rewrite it in Perl, but didn't finish it.

@edolstra
Copy link
Member

edolstra commented Nov 1, 2018

So that one script effectively increases closure sizes by 55 MiB.

@grahamc
Copy link
Member

grahamc commented Nov 1, 2018

@FRidh I just bisected to that very commit. Tough :/

@andrew-d
Copy link
Contributor

andrew-d commented Nov 1, 2018

@edolstra - OOC, is the primary concern closure size? Any thoughts on a more acceptable number (e.g. is a 5MB increase okay)?

@bgamari
Copy link
Contributor Author

bgamari commented Nov 1, 2018

@edolstra,j while I'm not attached to Python in particular, I really don't see the sense in using Perl over Python. If anything it seems to me that the residual Perl should be moved to Python, not the other way around. My reasons are three-fold:

  • Python has a sensible static analysis/typechecking story, which Perl (AFAIK) does lacks. I have been pretty impressed by how much mypy can catch.
  • Perl is, frankly, hell to cross-compile and very little can be done to fix this. The language and its ecosystem were simply developed with very little consideration for cross-compilation.
  • In 2018 it is much easier to find people to work in Python than Perl

A compiled language (Rust or C++) also would be fine here. Really, just anything but Perl.

@edolstra
Copy link
Member

edolstra commented Nov 1, 2018

@andrew-d No, the main concern is not doing gratuitous rewrites. The motivation (cross-compilation) is a weak one because NixOS does not support cross-compilation.

Also, I find Python a horrible language to maintain (worse than Perl, even). Rewriting NixOps in Python was the worst decision I ever made. Never again.

@edolstra
Copy link
Member

edolstra commented Nov 1, 2018

rust/go binaries would be even larger in size then scripts

A Rust binary would not be bigger than the 50+ MiB that Python adds to the closure size. All the current Perl scripts (switch-to-configuration.pl, setup-etc.pl, install-grub.pl, nixos-generate-config.pl, update-users-groups.pl, command-not-found.pl) could be replaced by a single Rust binary that shouldn't be bigger than a megabyte or two.

@bgamari
Copy link
Contributor Author

bgamari commented Nov 2, 2018

No, the main concern is not doing gratuitous rewrites. The motivation (cross-compilation) is a weak one because NixOS does not support cross-compilation.

I'm not sure what you mean by this; Both @Ericson2314 and I have put quite a bit of work into fixing cross-compilation in both Nixpkgs and NixOS. However, at this point I cross-compile NixOS routinely and use it to support a client project.

@Ericson2314
Copy link
Member

I give others credit for NixOS in particular :). But yeah as I understand it the only obstical remaining is Perl.

I don't love either but Python > Perl is also by far the more common opinion as others have said, and that should be taken into account.

I don't think Rust buys us much either. I'm a huge believer in static types, but if everything but the dbus part is shelling out, it's still fundamentally stringly typed. Hardening non-weakest links is a rather pointless exercise.

Still, if Rust is the best way for this to move forward, so be it. I haven't looked that much in the nixpkgs some of things, but in principle it should work very well.

@edolstra
Copy link
Member

edolstra commented Nov 5, 2018

Does that actually work at the moment? The shebang paths in switch-to-configuration and other scripts in 64-bit closures won't work on 32-bit systems either.

To be honest I don't think it's a use case that needs to be a hard requirement.

@Mic92
Copy link
Member

Mic92 commented Nov 28, 2018

Rust would be soon usable w.r.t to cross compiling: #50866

@zimbatm
Copy link
Member

zimbatm commented Nov 28, 2018

We can also rewrite those scripts in Bash, bash has a small closure size ;)

@Mic92
Copy link
Member

Mic92 commented Nov 29, 2018

They were in bash before, but where rewritten in perl because they were too slow according to the git history.

@bgamari
Copy link
Contributor Author

bgamari commented Dec 7, 2018

Concerning

It would be nice to rewrite stdenv builders from bash to perl

I would just like to repeat my previous point:

Perl is, frankly, hell to cross-compile and very little can be done to fix this. The language and its ecosystem were simply developed with very little consideration for cross-compilation.

I would hope that this disqualifies the language from any use in something as central as the stdenv builders.

@danbst
Copy link
Contributor

danbst commented Jan 2, 2019

@edolstra

Also, I find Python a horrible language to maintain (worse than Perl, even). Rewriting NixOps in Python was the worst decision I ever made. Never again.

This is unique experience. Can you write-up a separate post on "why"? Or is it more personal, so nothing to share?

@rnhmjoj
Copy link
Contributor

rnhmjoj commented Jan 7, 2021

This is unique experience. Can you write-up a separate post on "why"? Or is it more personal, so nothing to share?

If I had to guess, breaking changes in the standard library between major/minor releases.
Perl is really stable: you can take 25yo script and it will still run unchanged.

@vobloeb
Copy link
Contributor

vobloeb commented Jan 10, 2021

Besides increasing minimal system closure size, switching to python would increase number of packets required to cross-compile a system closure: Python + some libs + deps would need to be cross-compilable.

I nice idea I think is to rewrite switch-to-configuration in C++ and build as i686 static executable.
Although it might put the readability even beyond perl, it would:

  1. decrease minimal system closure size
  2. allow to nix-rebuild boot; reboot (and to nix-infect) x64_86 closures on top of i686 installations effectively upgrading them to 64-bit. Currently switch-to-configuration in x64_86 closure requires 64-bit perl which does not run on i686, so nix-rebuild boot; reboot works only in one direction (x64_86 -> i686 using 32-bit perl which runs on both). switch-to-configuration in C++ would enable both directions.

@vobloeb
Copy link
Contributor

vobloeb commented Jan 10, 2021

@edolstra

Also, I find Python a horrible language to maintain (worse than Perl, even). Rewriting NixOps in Python was the worst decision I ever made. Never again.

This is unique experience. Can you write-up a separate post on "why"? Or is it more personal, so nothing to share?

That raises questions how the decision on test-driver perl->python rewrite had been passed and why @edolstra did not vetoed it (I agree that as embed language python is terrible + having tab-language within Nix string literals with non-trivial indent stripping and escape sequences is awful)

@Mic92
Copy link
Member

Mic92 commented Jan 10, 2021

@edolstra

Also, I find Python a horrible language to maintain (worse than Perl, even). Rewriting NixOps in Python was the worst decision I ever made. Never again.

This is unique experience. Can you write-up a separate post on "why"? Or is it more personal, so nothing to share?

That raises questions how the decision on test-driver perl->python rewrite had been passed and why @edolstra did not vetoed it (I agree that as embed language python is terrible + having tab-language within Nix string literals with non-trivial indent stripping and escape sequences is awful)

Because not many contributors know perl anymore.

@flokli
Copy link
Member

flokli commented Jan 10, 2021

Also note perl is probably worse when it comes to cross-compilation than python.

@vobloeb
Copy link
Contributor

vobloeb commented Jan 10, 2021

Also note perl is probably worse when it comes to cross-compilation than python.

Yes, we depend on https://arsv.github.io/perl-cross/ which is not very reliable. The project is not very popular and they might stop supporting it at any moment. That's +1 for C++

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (update) This PR changes an existing module in `nixos/` 10.rebuild-darwin: 501+ This PR causes many rebuilds on Darwin and should normally target the staging branches. 10.rebuild-linux: 501+ This PR causes many rebuilds on Linux and should normally target the staging branches.

Projects

None yet

Development

Successfully merging this pull request may close these issues.