1212from string import Template
1313from subprocess import PIPE , CalledProcessError , Popen
1414from threading import Thread
15- from typing import IO , TYPE_CHECKING , Literal , assert_never , overload , override
15+ from typing import IO , TYPE_CHECKING , Literal , Self , assert_never , overload , override
1616
1717from utilities .atomicwrites import (
1818 _CopySourceNotFoundError ,
@@ -547,6 +547,66 @@ def expand_path(
547547##
548548
549549
550+ def getent (user : str , / ) -> GetEntOutput :
551+ """Get an entry from Name Service Switch libraries."""
552+ text = run (* getent_cmd (user ), return_ = True ) # skipif-not-linux
553+ return GetEntOutput .parse (text ) # skipif-not-linux
554+
555+
556+ def getent_cmd (user : str , / ) -> list [str ]:
557+ """Command to use 'getent' to get entries from Name Service Switch libraries."""
558+ return ["getent" , "passwd" , user ]
559+
560+
561+ def getent_ssh (
562+ ssh_user : str ,
563+ hostname : str ,
564+ / ,
565+ * ,
566+ user : str | None = None ,
567+ retry : Retry | None = None ,
568+ logger : LoggerLike | None = None ,
569+ ) -> GetEntOutput :
570+ """Get an entry from Name Service Switch libraries."""
571+ user_use = ssh_user if user is None else user # skipif-ci
572+ text = ssh ( # skipif-ci
573+ ssh_user ,
574+ hostname ,
575+ * getent_cmd (user_use ),
576+ return_ = True ,
577+ retry = retry ,
578+ logger = logger ,
579+ )
580+ return GetEntOutput .parse (text ) # skipif-ci
581+
582+
583+ @dataclass (order = True , unsafe_hash = True , kw_only = True , slots = True )
584+ class GetEntOutput :
585+ username : str
586+ passwd : str
587+ uid : int
588+ gid : int
589+ gecos : str
590+ home : Path
591+ shell : Path
592+
593+ @classmethod
594+ def parse (cls , text : str , / ) -> Self :
595+ username , passwd , uid , gid , gecos , home , shell = text .split (":" )
596+ return cls (
597+ username = username ,
598+ passwd = passwd ,
599+ uid = int (uid ),
600+ gid = int (gid ),
601+ gecos = gecos ,
602+ home = Path (home ),
603+ shell = Path (shell ),
604+ )
605+
606+
607+ ##
608+
609+
550610def git_branch_current (path : PathLike , / ) -> str :
551611 """Show the current a branch."""
552612 return run (* GIT_BRANCH_SHOW_CURRENT , cwd = path , return_ = True )
@@ -2316,9 +2376,10 @@ def yield_ssh_temp_dir(
23162376 keep : bool = False ,
23172377) -> Iterator [Path ]:
23182378 """Yield a temporary directory on a remote machine."""
2319- path = Path ( # skipif-ci
2320- ssh ( user , hostname , * MKTEMP_DIR_CMD , return_ = True , retry = retry , logger = logger )
2379+ text = ssh ( # skipif-ci
2380+ user , hostname , * MKTEMP_DIR_CMD , return_ = True , retry = retry , logger = logger
23212381 )
2382+ path = Path (text ) # skipif-ci
23222383 try : # skipif-ci
23232384 yield path
23242385 finally : # skipif-ci
@@ -2344,6 +2405,8 @@ def yield_ssh_temp_dir(
23442405 "UPDATE_CA_CERTIFICATES" ,
23452406 "ChownCmdError" ,
23462407 "CpError" ,
2408+ "GetEntOutput" ,
2409+ "GetEntOutput" ,
23472410 "MvFileError" ,
23482411 "RsyncCmdError" ,
23492412 "RsyncCmdNoSourcesError" ,
@@ -2371,6 +2434,9 @@ def yield_ssh_temp_dir(
23712434 "echo_cmd" ,
23722435 "env_cmds" ,
23732436 "expand_path" ,
2437+ "getent" ,
2438+ "getent_cmd" ,
2439+ "getent_ssh" ,
23742440 "git_branch_current" ,
23752441 "git_checkout" ,
23762442 "git_checkout_cmd" ,
0 commit comments