Skip to content

Commit c9ea823

Browse files
committed
Doc and test tweaks for ENVPath functions
Reword AppendENVPath and PrependENVPath manpage entries. Add more unit tests for the underlying functions, SCons.Util.AppendPath and SCons.Util.PrependPath Signed-off-by: Mats Wichmann <[email protected]>
1 parent 94ad058 commit c9ea823

File tree

4 files changed

+146
-88
lines changed

4 files changed

+146
-88
lines changed

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
156156
- Improve the wording of Configure methods.
157157
- Add unit test cases for AppendUnique, PrependUnique - verify behavior
158158
if existing value already contained more than one of added value.
159+
- Improve thw wording of AppendENVPath and PrependENVPath in manpage.
160+
- Add more unit tets to internal AppendPath, PrependPath functions.
159161

160162

161163
RELEASE 4.9.1 - Thu, 27 Mar 2025 11:40:20 -0700

RELEASE.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ DOCUMENTATION
126126

127127
- Improve the wording of Configure methods.
128128

129+
- Improve thw wording of AppendENVPath and PrependENVPath in manpage.
130+
129131
DEVELOPMENT
130132
-----------
131133

@@ -176,6 +178,8 @@ DEVELOPMENT
176178
iterative speedup test from 200 to 250. Increasing the workload
177179
should reduce the likelihood that the ninja tests are slower.
178180

181+
- Add more unit tets to internal AppendPath, PrependPath functions.
182+
179183
Thanks to the following contributors listed below for their contributions to this release.
180184
==========================================================================================
181185
.. code-block:: text

SCons/Environment.xml

Lines changed: 54 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -778,26 +778,32 @@ See also &f-link-env-AppendUnique;,
778778
</arguments>
779779
<summary>
780780
<para>
781-
Append path elements specified by <parameter>newpath</parameter>
782-
to the given search path string or list <parameter>name</parameter>
783-
in mapping <parameter>envname</parameter> in the &consenv;.
784-
Supplying <parameter>envname</parameter> is optional:
785-
the default is the execution environment &cv-link-ENV;.
786-
Optional <parameter>sep</parameter> is used as the search path separator,
787-
the default is the platform's separator (<systemitem>os.pathsep</systemitem>).
788-
A path element will only appear once.
789-
Any duplicates in <parameter>newpath</parameter> are dropped,
790-
keeping the last appearing (to preserve path order).
791-
If <parameter>delete_existing</parameter>
792-
is <constant>False</constant> (the default)
793-
any addition duplicating an existing path element is ignored;
794-
if <parameter>delete_existing</parameter>
795-
is <constant>True</constant> the existing value will
796-
be dropped and the path element will be added at the end.
797-
To help maintain uniqueness all paths are normalized (using
798-
<systemitem>os.path.normpath</systemitem>
799-
and
800-
<systemitem>os.path.normcase</systemitem>).
781+
Append one or more path prefixes to an entry in a dictionary-valued
782+
&consvar; in <parameter>env</parameter>.
783+
The &consvar; to update is <literal>"ENV"</literal>,
784+
unless <parameter>envname</parameter> is specified.
785+
The required <parameter>name</parameter> parameter
786+
gives the dictionary entry to update.
787+
The prefix(es) in <parameter>newpath</parameter>
788+
may be specified as a string,
789+
a directory node, or a list of strings.
790+
If a string, it may contain multiple prefixes separated
791+
by the system path separator
792+
(<systemitem>os.pathsep</systemitem>),
793+
or, if specified, by the value of <parameter>sep</parameter>.
794+
Top-relative path strings (starting with <literal>#</literal>) are recognized.
795+
</para>
796+
<para>
797+
Path prefixes will only appear once.
798+
Duplicate prefixes in <parameter>newpath</parameter> are removed,
799+
preserving the last occurrence to maintain path order.
800+
If <parameter>delete_existing</parameter> is true,
801+
existing duplicates are removed before appending;
802+
otherwise, new duplicates are skipped
803+
(the default is false).
804+
Prefixes are normalized for comparisons to avoid problems of
805+
"same path, spelled differently",
806+
but retain their original form in the result.
801807
</para>
802808

803809
<para>
@@ -1119,8 +1125,8 @@ env4 = env.Clone(tools=['msvc', MyTool])
11191125
The
11201126
<parameter>parse_flags</parameter>
11211127
keyword argument is also recognized, to allow merging command-line
1122-
style arguments into the appropriate construction
1123-
variables (see &f-link-env-MergeFlags;).
1128+
style arguments into the appropriate &consvars;
1129+
(see &f-link-env-MergeFlags;).
11241130
</para>
11251131

11261132
<example_commands>
@@ -2863,26 +2869,32 @@ and &f-link-env-PrependUnique;.
28632869
</arguments>
28642870
<summary>
28652871
<para>
2866-
Prepend path elements specified by <parameter>newpath</parameter>
2867-
to the given search path string or list <parameter>name</parameter>
2868-
in mapping <parameter>envname</parameter> in the &consenv;.
2869-
Supplying <parameter>envname</parameter> is optional:
2870-
the default is the execution environment &cv-link-ENV;.
2871-
Optional <parameter>sep</parameter> is used as the search path separator,
2872-
the default is the platform's separator (<systemitem>os.pathsep</systemitem>).
2873-
A path element will only appear once.
2874-
Any duplicates in <parameter>newpath</parameter> are dropped,
2875-
keeping the first appearing (to preserve path order).
2876-
If <parameter>delete_existing</parameter>
2877-
is <constant>False</constant>
2878-
any addition duplicating an existing path element is ignored;
2879-
if <parameter>delete_existing</parameter>
2880-
is <constant>True</constant> (the default) the existing value will
2881-
be dropped and the path element will be inserted at the beginning.
2882-
To help maintain uniqueness all paths are normalized (using
2883-
<systemitem>os.path.normpath</systemitem>
2884-
and
2885-
<systemitem>os.path.normcase</systemitem>).
2872+
Prepend one or more path prefixes to an entry in a dictionary-valued
2873+
&consvar; in <parameter>env</parameter>.
2874+
The &consvar; to update is <literal>"ENV"</literal>,
2875+
unless <parameter>envname</parameter> is specified.
2876+
The required <parameter>name</parameter> parameter
2877+
gives the dictionary entry to update.
2878+
The prefix(es) in <parameter>newpath</parameter>
2879+
may be specified as a string,
2880+
a directory node, or a list of strings.
2881+
If a string, it may contain multiple prefixes separated
2882+
by the system path separator
2883+
(<systemitem>os.pathsep</systemitem>),
2884+
or, if specified, by the value of <parameter>sep</parameter>.
2885+
Top-relative path strings (starting with <literal>#</literal>) are recognized.
2886+
</para>
2887+
<para>
2888+
Path prefixes will only appear once.
2889+
Duplicate prefixes in <parameter>newpath</parameter> are removed,
2890+
preserving the last occurrence to maintain path order.
2891+
If <parameter>delete_existing</parameter> is true,
2892+
existing duplicates are removed before prepending;
2893+
otherwise, new duplicates are skipped
2894+
(the default is false).
2895+
Prefixes are normalized for comparisons to avoid problems of
2896+
"same path, spelled differently",
2897+
but retain their original form in the result.
28862898
</para>
28872899

28882900
<para>

SCons/Util/UtilTests.py

Lines changed: 86 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -545,55 +545,95 @@ def test_get_native_path(self) -> None:
545545

546546
def test_PrependPath(self) -> None:
547547
"""Test prepending to a path"""
548-
p1: list | str = r'C:\dir\num\one;C:\dir\num\two'
549-
p2: list | str = r'C:\mydir\num\one;C:\mydir\num\two'
550-
# have to include the pathsep here so that the test will work on UNIX too.
551-
p1 = PrependPath(p1, r'C:\dir\num\two', sep=';')
552-
p1 = PrependPath(p1, r'C:\dir\num\three', sep=';')
553-
assert p1 == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one', p1
554-
555-
p2 = PrependPath(p2, r'C:\mydir\num\three', sep=';')
556-
p2 = PrependPath(p2, r'C:\mydir\num\one', sep=';')
557-
assert p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two', p2
558-
559-
# check (only) first one is kept if there are dupes in new
560-
p3: list | str = r'C:\dir\num\one'
561-
p3 = PrependPath(p3, r'C:\dir\num\two;C:\dir\num\three;C:\dir\num\two', sep=';')
562-
assert p3 == r'C:\dir\num\two;C:\dir\num\three;C:\dir\num\one', p3
548+
# have to specify the pathsep when adding so it's cross-platform
549+
# new duplicates existing - "moves to front"
550+
with self.subTest():
551+
p1: list | str = r'C:\dir\num\one;C:\dir\num\two'
552+
p1 = PrependPath(p1, r'C:\dir\num\two', sep=';')
553+
p1 = PrependPath(p1, r'C:\dir\num\three', sep=';')
554+
self.assertEqual(p1, r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
555+
556+
# ... except with delete_existing false
557+
with self.subTest():
558+
p2: list | str = r'C:\dir\num\one;C:\dir\num\two'
559+
p2 = PrependPath(p2, r'C:\dir\num\two', sep=';', delete_existing=False)
560+
p2 = PrependPath(p2, r'C:\dir\num\three', sep=';', delete_existing=False)
561+
self.assertEqual(p2, r'C:\dir\num\three;C:\dir\num\one;C:\dir\num\two')
562+
563+
# only last one is kept if there are dupes in new
564+
with self.subTest():
565+
p3: list | str = r'C:\dir\num\one'
566+
p3 = PrependPath(p3, r'C:\dir\num\two;C:\dir\num\three;C:\dir\num\two', sep=';')
567+
self.assertEqual(p3, r'C:\dir\num\two;C:\dir\num\three;C:\dir\num\one')
568+
569+
# try prepending a Dir Node
570+
with self.subTest():
571+
p4: list | str = r'C:\dir\num\one'
572+
test = TestCmd.TestCmd(workdir='')
573+
test.subdir('sub')
574+
subdir = test.workpath('sub')
575+
p4 = PrependPath(p4, subdir, sep=';')
576+
self.assertEqual(p4, rf'{subdir};C:\dir\num\one')
577+
578+
# try with initial list, adding string (result stays a list)
579+
with self.subTest():
580+
p5: list = [r'C:\dir\num\one', r'C:\dir\num\two']
581+
p5 = PrependPath(p5, r'C:\dir\num\two', sep=';')
582+
self.assertEqual(p5, [r'C:\dir\num\two', r'C:\dir\num\one'])
583+
p5 = PrependPath(p5, r'C:\dir\num\three', sep=';')
584+
self.assertEqual(p5, [r'C:\dir\num\three', r'C:\dir\num\two', r'C:\dir\num\one'])
585+
586+
# try with initial string, adding list (result stays a string)
587+
with self.subTest():
588+
p6: list | str = r'C:\dir\num\one;C:\dir\num\two'
589+
p6 = PrependPath(p6, [r'C:\dir\num\two', r'C:\dir\num\three'], sep=';')
590+
self.assertEqual(p6, r'C:\dir\num\two;C:\dir\num\three;C:\dir\num\one')
591+
563592

564593
def test_AppendPath(self) -> None:
565594
"""Test appending to a path."""
566-
p1: list | str = r'C:\dir\num\one;C:\dir\num\two'
567-
p2: list | str = r'C:\mydir\num\one;C:\mydir\num\two'
568-
# have to include the pathsep here so that the test will work on UNIX too.
569-
p1 = AppendPath(p1, r'C:\dir\num\two', sep=';')
570-
p1 = AppendPath(p1, r'C:\dir\num\three', sep=';')
571-
assert p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three', p1
572-
573-
p2 = AppendPath(p2, r'C:\mydir\num\three', sep=';')
574-
p2 = AppendPath(p2, r'C:\mydir\num\one', sep=';')
575-
assert p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one', p2
576-
577-
# check (only) last one is kept if there are dupes in new
578-
p3: list | str = r'C:\dir\num\one'
579-
p3 = AppendPath(p3, r'C:\dir\num\two;C:\dir\num\three;C:\dir\num\two', sep=';')
580-
assert p3 == r'C:\dir\num\one;C:\dir\num\three;C:\dir\num\two', p3
581-
582-
def test_PrependPathPreserveOld(self) -> None:
583-
"""Test prepending to a path while preserving old paths"""
584-
p1: list | str = r'C:\dir\num\one;C:\dir\num\two'
585-
# have to include the pathsep here so that the test will work on UNIX too.
586-
p1 = PrependPath(p1, r'C:\dir\num\two', sep=';', delete_existing=False)
587-
p1 = PrependPath(p1, r'C:\dir\num\three', sep=';')
588-
assert p1 == r'C:\dir\num\three;C:\dir\num\one;C:\dir\num\two', p1
589-
590-
def test_AppendPathPreserveOld(self) -> None:
591-
"""Test appending to a path while preserving old paths"""
592-
p1: list | str = r'C:\dir\num\one;C:\dir\num\two'
593-
# have to include the pathsep here so that the test will work on UNIX too.
594-
p1 = AppendPath(p1, r'C:\dir\num\one', sep=';', delete_existing=False)
595-
p1 = AppendPath(p1, r'C:\dir\num\three', sep=';')
596-
assert p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three', p1
595+
# have to specify the pathsep when adding so it's cross-platform
596+
# new duplicates existing - "moves to end"
597+
with self.subTest():
598+
p1: list | str = r'C:\dir\num\one;C:\dir\num\two'
599+
p1 = AppendPath(p1, r'C:\dir\num\two', sep=';')
600+
p1 = AppendPath(p1, r'C:\dir\num\three', sep=';')
601+
self.assertEqual(p1, r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
602+
603+
# ... except with delete_existing false
604+
with self.subTest():
605+
p2: list | str = r'C:\dir\num\one;C:\dir\num\two'
606+
p2 = AppendPath(p1, r'C:\dir\num\one', sep=';', delete_existing=False)
607+
p2 = AppendPath(p1, r'C:\dir\num\three', sep=';')
608+
self.assertEqual(p2, r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
609+
610+
# only last one is kept if there are dupes in new
611+
with self.subTest():
612+
p3: list | str = r'C:\dir\num\one'
613+
p3 = AppendPath(p3, r'C:\dir\num\two;C:\dir\num\three;C:\dir\num\two', sep=';')
614+
self.assertEqual(p3, r'C:\dir\num\one;C:\dir\num\three;C:\dir\num\two')
615+
616+
# try appending a Dir Node
617+
with self.subTest():
618+
p4: list | str = r'C:\dir\num\one'
619+
test = TestCmd.TestCmd(workdir='')
620+
test.subdir('sub')
621+
subdir = test.workpath('sub')
622+
p4 = AppendPath(p4, subdir, sep=';')
623+
self.assertEqual(p4, rf'C:\dir\num\one;{subdir}')
624+
625+
# try with initial list, adding string (result stays a list)
626+
with self.subTest():
627+
p5: list = [r'C:\dir\num\one', r'C:\dir\num\two']
628+
p5 = AppendPath(p5, r'C:\dir\num\two', sep=';')
629+
p5 = AppendPath(p5, r'C:\dir\num\three', sep=';')
630+
self.assertEqual(p5, [r'C:\dir\num\one', r'C:\dir\num\two', r'C:\dir\num\three'])
631+
632+
# try with initia string, adding list (result stays a string)
633+
with self.subTest():
634+
p6: list | str = r'C:\dir\num\one;C:\dir\num\two'
635+
p6 = AppendPath(p6, [r'C:\dir\num\two', r'C:\dir\num\three'], sep=';')
636+
self.assertEqual(p6, r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
597637

598638
def test_addPathIfNotExists(self) -> None:
599639
"""Test the AddPathIfNotExists() function"""

0 commit comments

Comments
 (0)