private: No

import argparse
import os
import subprocess
import sys
from tempfile import TemporaryDirectory
from typing import Tuple, Optional, List, Union

# since os.PathLike breaks python typechecking
path = bytes
drv_output = Tuple[path, bytes]

def add_root_args(gcroots_dir: Optional[bytes],
                  symlink_name: path) -> List[Union[path, str]]:
    if gcroots_dir is None:
        return []
        return ['--add-root',
                os.path.join(gcroots_dir, symlink_name),

def parse_drv_path(path: path) -> Optional[drv_output]:
    (nix_store, drv_file) = os.path.split(path)

    # drv “filenames” printed by nix-instantiate are of the form
    # ${hash}-${name}.drv[!${output}] where name, hash and output
    # may not contain any '!' among other things
    drv_parts = drv_file.split(b'!')
    if len(drv_parts) > 2:
        return None
    elif len(drv_parts) == 1:
        # default output
        return (path, b'out')
        return (os.path.join(nix_store, drv_parts[0]), drv_parts[1])

def derivation_outputs(attr: str, gcroots_dir: Optional[path] = None
                       ) -> List[drv_output]:
    cmd: List[Union[str, path]] = ['nix-instantiate', '-Q', '-E']

    # TODO(sterni): make this work with custom stuff like vuizuvi and depot
    expr = """
          pkgs = import <nixpkgs> {{}};
 (o: pkgs.{attr}."${{o}}") pkgs.{attr}.outputs

    cmd.extend(add_root_args(gcroots_dir, b'instantiation-result'))

    instantiate =, capture_output=True)

    if instantiate.returncode != 0:
        print(instantiate.stderr, file=sys.stderr)
        return []
        return [o for o in map(parse_drv_path, instantiate.stdout.splitlines())
                if o is not None]

def main():
    parser = argparse.ArgumentParser(description='nix-shell for man pages')
    parser.add_argument('attribute', metavar='ATTR',
                        help='nixpkgs attribute holding the desired package')
    parser.add_argument('section_or_page', metavar='SECTION|PAGE',
    parser.add_argument('page', metavar='PAGE', nargs='?')

    args = parser.parse_args()

    # first arg is always the derivation attribute
    drv_attr = args.attribute

    # second arg is either the page or the section number
    if args.section_or_page is not None and args.section_or_page.isdigit():
        section = int(args.section_or_page)
        page = None
        section = 1
        page = args.section_or_page

    # last arg is the page. if it is missing and was not
    # given as the second, use drv_attr
    if is not None:
        page =
    elif page is None:
        page = drv_attr

    print('{}({}) from pkgs.{}'.format(page, section, drv_attr))

    with TemporaryDirectory(prefix='nman-') as gcroots:
        outputs = derivation_outputs(drv_attr,

        for (p, o) in outputs:
            print('{}: {}'.format(o, os.path.realpath(p)))

if __name__ == '__main__':