22import functools
33
44from typing import Dict , Union
5+ from typing import runtime_checkable
6+ from typing import Protocol
57
68
79####
8- # from jaraco.path 3.4 .1
10+ # from jaraco.path 3.7 .1
911
10- FilesSpec = Dict [str , Union [str , bytes , 'FilesSpec' ]] # type: ignore
1112
13+ class Symlink (str ):
14+ """
15+ A string indicating the target of a symlink.
16+ """
17+
18+
19+ FilesSpec = Dict [str , Union [str , bytes , Symlink , 'FilesSpec' ]]
20+
21+
22+ @runtime_checkable
23+ class TreeMaker (Protocol ):
24+ def __truediv__ (self , * args , ** kwargs ): ... # pragma: no cover
25+
26+ def mkdir (self , ** kwargs ): ... # pragma: no cover
27+
28+ def write_text (self , content , ** kwargs ): ... # pragma: no cover
29+
30+ def write_bytes (self , content ): ... # pragma: no cover
1231
13- def build (spec : FilesSpec , prefix = pathlib .Path ()):
32+ def symlink_to (self , target ): ... # pragma: no cover
33+
34+
35+ def _ensure_tree_maker (obj : Union [str , TreeMaker ]) -> TreeMaker :
36+ return obj if isinstance (obj , TreeMaker ) else pathlib .Path (obj ) # type: ignore[return-value]
37+
38+
39+ def build (
40+ spec : FilesSpec ,
41+ prefix : Union [str , TreeMaker ] = pathlib .Path (), # type: ignore[assignment]
42+ ):
1443 """
1544 Build a set of files/directories, as described by the spec.
1645
@@ -25,21 +54,25 @@ def build(spec: FilesSpec, prefix=pathlib.Path()):
2554 ... "__init__.py": "",
2655 ... },
2756 ... "baz.py": "# Some code",
28- ... }
57+ ... "bar.py": Symlink("baz.py"),
58+ ... },
59+ ... "bing": Symlink("foo"),
2960 ... }
3061 >>> target = getfixture('tmp_path')
3162 >>> build(spec, target)
3263 >>> target.joinpath('foo/baz.py').read_text(encoding='utf-8')
3364 '# Some code'
65+ >>> target.joinpath('bing/bar.py').read_text(encoding='utf-8')
66+ '# Some code'
3467 """
3568 for name , contents in spec .items ():
36- create (contents , pathlib . Path (prefix ) / name )
69+ create (contents , _ensure_tree_maker (prefix ) / name )
3770
3871
3972@functools .singledispatch
4073def create (content : Union [str , bytes , FilesSpec ], path ):
4174 path .mkdir (exist_ok = True )
42- build (content , prefix = path ) # type: ignore
75+ build (content , prefix = path ) # type: ignore[arg-type]
4376
4477
4578@create .register
@@ -52,5 +85,10 @@ def _(content: str, path):
5285 path .write_text (content , encoding = 'utf-8' )
5386
5487
88+ @create .register
89+ def _ (content : Symlink , path ):
90+ path .symlink_to (content )
91+
92+
5593# end from jaraco.path
5694####
0 commit comments