Skip to content

Commit b8fffb3

Browse files
authored
Merge pull request SCons#4479 from mwichmann/doc/PyPackageDir
Fix PyPackageDir
2 parents c03c104 + 00ef03a commit b8fffb3

File tree

5 files changed

+73
-25
lines changed

5 files changed

+73
-25
lines changed

CHANGES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
8484
- Fix bad typing in Action.py: process() and strfunction().
8585
- Add Pseudo() to global functions, had been omitted. Fixes #4474.
8686
The Pseudo manpage entry was updated to provide more clarity.
87+
- The internal routine which implements the PyPackageDir function
88+
would fail with an exception if called with a module which is
89+
not found. It will now return None. Updated manpage entry and
90+
docstring..
8791

8892

8993
RELEASE 4.6.0 - Sun, 19 Nov 2023 17:22:20 -0700

RELEASE.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ FIXES
5454
make sure decoding of bytes doesn't fail.
5555
- Documentation indicated that both Pseudo() and env.Pseudo() were usable,
5656
but Pseudo() did not work; is now enabled.
57+
- PyPackageDir no longer fails if passed a module name which cannot be found,
58+
now returns None.
59+
5760

5861
IMPROVEMENTS
5962
------------

SCons/Environment.xml

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2880,20 +2880,41 @@ and &f-link-env-Prepend;.
28802880
</arguments>
28812881
<summary>
28822882
<para>
2883-
This returns a Directory Node similar to Dir.
2884-
The python module / package is looked up and if located
2885-
the directory is returned for the location.
2886-
<parameter>modulename</parameter>
2887-
Is a named python package / module to
2888-
lookup the directory for it's location.
2889-
</para>
2890-
<para>
2891-
If
2892-
<parameter>modulename</parameter>
2893-
is a list, SCons returns a list of Dir nodes.
2883+
Finds the location of <parameter>modulename</parameter>,
2884+
which can be a string or a sequence of strings,
2885+
each representing the name of a &Python; module.
28942886
Construction variables are expanded in
28952887
<parameter>modulename</parameter>.
2888+
Returns a Directory Node (see &f-link-Dir;),
2889+
or a list of Directory Nodes if
2890+
<parameter>modulename</parameter> is a sequence.
2891+
<literal>None</literal> is returned for any module not found.
2892+
</para>
2893+
2894+
<para>
2895+
When a Tool module which is installed as a
2896+
&Python; module is used, you need
2897+
to specify a <parameter>toolpath</parameter> argument to
2898+
&f-link-Tool;,
2899+
&f-link-Environment;
2900+
or &f-link-Clone;,
2901+
as tools outside the standard project locations
2902+
(<filename>site_scons/site_tools</filename>)
2903+
will not be found otherwise.
2904+
Using &f-PyPackageDir; allows this path to be
2905+
discovered at runtime instead of hardcoding the path.
2906+
</para>
2907+
2908+
<para>
2909+
Example:
28962910
</para>
2911+
2912+
<example_commands>
2913+
env = Environment(
2914+
tools=["default", "ExampleTool"],
2915+
toolpath=[PyPackageDir("example_tool")]
2916+
)
2917+
</example_commands>
28972918
</summary>
28982919
</scons_function>
28992920

SCons/Node/FS.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,7 +1294,7 @@ def get_root(self, drive):
12941294
self.Root[''] = root
12951295
return root
12961296

1297-
def _lookup(self, p, directory, fsclass, create: int=1):
1297+
def _lookup(self, p, directory, fsclass, create: bool = True):
12981298
"""
12991299
The generic entry point for Node lookup with user-supplied data.
13001300
@@ -1430,7 +1430,7 @@ def _lookup(self, p, directory, fsclass, create: int=1):
14301430

14311431
return root._lookup_abs(p, fsclass, create)
14321432

1433-
def Entry(self, name, directory = None, create: int = 1):
1433+
def Entry(self, name, directory = None, create: bool = True):
14341434
"""Look up or create a generic Entry node with the specified name.
14351435
If the name is a relative path (begins with ./, ../, or a file
14361436
name), then it is looked up relative to the supplied directory
@@ -1439,7 +1439,7 @@ def Entry(self, name, directory = None, create: int = 1):
14391439
"""
14401440
return self._lookup(name, directory, Entry, create)
14411441

1442-
def File(self, name, directory = None, create: int = 1):
1442+
def File(self, name, directory = None, create: bool = True):
14431443
"""Look up or create a File node with the specified name. If
14441444
the name is a relative path (begins with ./, ../, or a file name),
14451445
then it is looked up relative to the supplied directory node,
@@ -1486,21 +1486,24 @@ def Repository(self, *dirs) -> None:
14861486
d = self.Dir(d)
14871487
self.Top.addRepository(d)
14881488

1489-
def PyPackageDir(self, modulename):
1490-
r"""Locate the directory of a given python module name
1489+
def PyPackageDir(self, modulename) -> Optional[Dir]:
1490+
r"""Locate the directory of Python module *modulename*.
14911491
1492-
For example scons might resolve to
1493-
Windows: C:\Python27\Lib\site-packages\scons-2.5.1
1494-
Linux: /usr/lib/scons
1492+
For example 'SCons' might resolve to
1493+
Windows: C:\Python311\Lib\site-packages\SCons
1494+
Linux: /usr/lib64/python3.11/site-packages/SCons
14951495
1496-
This can be useful when we want to determine a toolpath based on a python module name"""
1496+
Can be used to determine a toolpath based on a Python module name.
14971497
1498-
dirpath = ''
1499-
1500-
# Python3 Code
1498+
This is the backend called by the public API function
1499+
:meth:`~Environment.Base.PyPackageDir`.
1500+
"""
15011501
modspec = importlib.util.find_spec(modulename)
1502-
dirpath = os.path.dirname(modspec.origin)
1503-
return self._lookup(dirpath, None, Dir, True)
1502+
if modspec:
1503+
origin = os.path.dirname(modspec.origin)
1504+
return self._lookup(origin, directory=None, fsclass=Dir, create=True)
1505+
else:
1506+
return None
15041507

15051508

15061509
def variant_dir_target_climb(self, orig, dir, tail):

SCons/Node/FSTests.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4046,6 +4046,23 @@ def test_root_lookup_equivalence(self) -> None:
40464046
os.chdir(save_cwd)
40474047

40484048

4049+
class PyPackageDir(unittest.TestCase):
4050+
def runTest(self) -> None:
4051+
"""Test calling the PyPackageDir() method.
4052+
4053+
We don't want to mock the positive case here - there's
4054+
testing for that in E2E test test/Dir/PyPackageDir.
4055+
We're only making sure we don't die in the negative case
4056+
(module not found) and instead return None.
4057+
"""
4058+
fs = SCons.Node.FS.FS('/')
4059+
try:
4060+
pkdir = fs.PyPackageDir("garglemod")
4061+
except AttributeError:
4062+
self.fail("non-existent module raised AttributeError")
4063+
self.assertIsNone(pkdir)
4064+
4065+
40494066
if __name__ == "__main__":
40504067
unittest.main()
40514068

0 commit comments

Comments
 (0)