Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Script/Main.py so it uses importlib #3552

Merged
merged 2 commits into from
Feb 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Accommodate VS 2017 Express - it's got a more liberal license then VS
Community, so some people prefer it (from 2019, no more Express)
- vswhere call should also now work even if programs aren't on the C: drive.
- Script/Main.py now uses importlib instead of imp module.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth adding motivation.. imp deprecated and will be removed in Py#.#?
Are we still using imp elsewhere?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to insert the word "deprecated" when you decide to push it that's fine. I don't think there's a timeline, they're still dickering about deprecation (sounds familiar!).

Deprecated since version 3.4: The imp package is pending deprecation in favor of importlib.

If you want me to update it from here I will.



RELEASE 3.1.2 - Mon, 17 Dec 2019 02:06:27 +0000
Expand Down
126 changes: 67 additions & 59 deletions src/engine/SCons/Script/Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@

import SCons.compat

import importlib.util
import os
import re
import sys
import time
import traceback
Expand All @@ -65,7 +67,6 @@
import SCons.Taskmaster
import SCons.Util
import SCons.Warnings

import SCons.Script.Interactive

# Global variables
Expand Down Expand Up @@ -698,80 +699,87 @@ def _create_path(plist):
return path

def _load_site_scons_dir(topdir, site_dir_name=None):
"""Load the site_scons dir under topdir.
Prepends site_scons to sys.path, imports site_scons/site_init.py,
and prepends site_scons/site_tools to default toolpath."""
"""Load the site directory under topdir.

If a site dir name is supplied use it, else use default "site_scons"
Prepend site dir to sys.path.
If a "site_tools" subdir exists, prepend to toolpath.
Import "site_init.py" from site dir if it exists.
"""
if site_dir_name:
err_if_not_found = True # user specified: err if missing
else:
site_dir_name = "site_scons"
err_if_not_found = False

err_if_not_found = False # scons default: okay to be missing
site_dir = os.path.join(topdir, site_dir_name)

if not os.path.exists(site_dir):
if err_if_not_found:
raise SCons.Errors.UserError("site dir %s not found."%site_dir)
raise SCons.Errors.UserError("site dir %s not found." % site_dir)
return
sys.path.insert(0, os.path.abspath(site_dir))

site_init_filename = "site_init.py"
site_init_modname = "site_init"
site_tools_dirname = "site_tools"
# prepend to sys.path
sys.path = [os.path.abspath(site_dir)] + sys.path
site_init_file = os.path.join(site_dir, site_init_filename)
site_tools_dir = os.path.join(site_dir, site_tools_dirname)
if os.path.exists(site_init_file):
import imp, re
try:
try:
fp, pathname, description = imp.find_module(site_init_modname,
[site_dir])
# Load the file into SCons.Script namespace. This is
# opaque and clever; m is the module object for the
# SCons.Script module, and the exec ... in call executes a
# file (or string containing code) in the context of the
# module's dictionary, so anything that code defines ends
# up adding to that module. This is really short, but all
# the error checking makes it longer.
try:
m = sys.modules['SCons.Script']
except Exception as e:
fmt = 'cannot import site_init.py: missing SCons.Script module %s'
raise SCons.Errors.InternalError(fmt % repr(e))
try:
sfx = description[0]
modname = os.path.basename(pathname)[:-len(sfx)]
site_m = {"__file__": pathname, "__name__": modname, "__doc__": None}
re_special = re.compile("__[^_]+__")
for k, v in m.__dict__.items():
if not re_special.match(k):
site_m[k] = v

# This is the magic.
exec(compile(fp.read(), fp.name, 'exec'), site_m)
except KeyboardInterrupt:
raise
except Exception as e:
fmt = '*** Error loading site_init file %s:\n'
sys.stderr.write(fmt % repr(site_init_file))
raise
else:
for k in site_m:
if not re_special.match(k):
m.__dict__[k] = site_m[k]
except KeyboardInterrupt:
raise
except ImportError as e:
fmt = '*** cannot import site init file %s:\n'
sys.stderr.write(fmt % repr(site_init_file))
raise
finally:
if fp:
fp.close()

if os.path.exists(site_tools_dir):
# prepend to DefaultToolpath
SCons.Tool.DefaultToolpath.insert(0, os.path.abspath(site_tools_dir))

if not os.path.exists(site_init_file):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the impact of moving this below the site_tools_dir if above?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just grouping, flow matches the reworded docstring.

return

# "import" the site_init.py file into the SCons.Script namespace.
# This is a variant on the basic Python import flow in that the globals
# dict for the compile step is prepopulated from the SCons.Script
# module object; on success the SCons.Script globals are refilled
# from the site_init globals so it all appears in SCons.Script
# instead of as a separate module.
try:
try:
m = sys.modules['SCons.Script']
except KeyError:
fmt = 'cannot import {}: missing SCons.Script module'
raise SCons.Errors.InternalError(fmt.format(site_init_file))

spec = importlib.util.spec_from_file_location(site_init_modname, site_init_file)
site_m = {
"__file__": spec.origin,
"__name__": spec.name,
"__doc__": None,
}
re_dunder = re.compile(r"__[^_]+__")
# update site dict with all but magic (dunder) methods
for k, v in m.__dict__.items():
if not re_dunder.match(k):
site_m[k] = v

with open(spec.origin, 'r') as f:
code = f.read()
try:
codeobj = compile(code, spec.name, "exec")
exec(codeobj, site_m)
except KeyboardInterrupt:
raise
except Exception:
fmt = "*** Error loading site_init file {}:\n"
sys.stderr.write(fmt.format(site_init_file))
raise
else:
# now refill globals with site_init's symbols
for k, v in site_m.items():
if not re_dunder.match(k):
m.__dict__[k] = v
except KeyboardInterrupt:
raise
except Exception:
fmt = "*** cannot import site init file {}:\n"
sys.stderr.write(fmt.format(site_init_file))
raise


def _load_all_site_scons_dirs(topdir, verbose=None):
"""Load all of the predefined site_scons dir.
Order is significant; we load them in order from most generic
Expand Down Expand Up @@ -810,7 +818,7 @@ def homedir(d):
sysdirs=['/usr/share/scons',
homedir('.scons')]

dirs=sysdirs + [topdir]
dirs = sysdirs + [topdir]
for d in dirs:
if verbose: # this is used by unit tests.
print("Loading site dir ", d)
Expand Down