package import: remove magic import line (#39183)

This commit is contained in:
Harmen Stoppels 2023-08-09 16:28:06 +02:00 committed by GitHub
parent 5e630174a1
commit 8e7c53a8ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 48 additions and 43 deletions

View file

@ -1266,12 +1266,8 @@ def make_stack(tb, stack=None):
# Point out the location in the install method where we failed.
filename = inspect.getfile(frame.f_code)
lineno = frame.f_lineno
if os.path.basename(filename) == "package.py":
# subtract 1 because we inject a magic import at the top of package files.
# TODO: get rid of the magic import.
lineno -= 1
lines = ["{0}:{1:d}, in {2}:".format(filename, lineno, frame.f_code.co_name)]
lines = [f"{filename}:{lineno:d}, in {frame.f_code.co_name}:"]
# Build a message showing context in the install method.
sourcelines, start = inspect.getsourcelines(frame)

View file

@ -78,43 +78,6 @@ def namespace_from_fullname(fullname):
return namespace
class _PrependFileLoader(importlib.machinery.SourceFileLoader):
def __init__(self, fullname, path, prepend=None):
super(_PrependFileLoader, self).__init__(fullname, path)
self.prepend = prepend
def path_stats(self, path):
stats = super(_PrependFileLoader, self).path_stats(path)
if self.prepend:
stats["size"] += len(self.prepend) + 1
return stats
def get_data(self, path):
data = super(_PrependFileLoader, self).get_data(path)
if path != self.path or self.prepend is None:
return data
else:
return self.prepend.encode() + b"\n" + data
class RepoLoader(_PrependFileLoader):
"""Loads a Python module associated with a package in specific repository"""
#: Code in ``_package_prepend`` is prepended to imported packages.
#:
#: Spack packages are expected to call `from spack.package import *`
#: themselves, but we are allowing a deprecation period before breaking
#: external repos that don't do this yet.
_package_prepend = "from spack.package import *"
def __init__(self, fullname, repo, package_name):
self.repo = repo
self.package_name = package_name
self.package_py = repo.filename_for_package_name(package_name)
self.fullname = fullname
super().__init__(self.fullname, self.package_py, prepend=self._package_prepend)
class SpackNamespaceLoader:
def create_module(self, spec):
return SpackNamespace(spec.name)
@ -155,7 +118,9 @@ def compute_loader(self, fullname):
# With 2 nested conditionals we can call "repo.real_name" only once
package_name = repo.real_name(module_name)
if package_name:
return RepoLoader(fullname, repo, package_name)
return importlib.machinery.SourceFileLoader(
fullname, repo.filename_for_package_name(package_name)
)
# We are importing a full namespace like 'spack.pkg.builtin'
if fullname == repo.full_namespace:
@ -1236,6 +1201,11 @@ def get_pkg_class(self, pkg_name):
raise UnknownPackageError(fullname)
except Exception as e:
msg = f"cannot load package '{pkg_name}' from the '{self.namespace}' repository: {e}"
if isinstance(e, NameError):
msg += (
". This usually means `from spack.package import *` "
"is missing at the top of the package.py file."
)
raise RepoError(msg) from e
cls = getattr(module, class_name)

View file

@ -145,6 +145,8 @@ def repo_with_changing_recipe(tmpdir_factory, mutable_mock_repo):
packages_dir = repo_dir.ensure("packages", dir=True)
root_pkg_str = """
from spack.package import *
class Root(Package):
homepage = "http://www.example.com"
url = "http://www.example.com/root-1.0.tar.gz"
@ -157,6 +159,8 @@ class Root(Package):
packages_dir.join("root", "package.py").write(root_pkg_str, ensure=True)
changing_template = """
from spack.package import *
class Changing(Package):
homepage = "http://www.example.com"
url = "http://www.example.com/changing-1.0.tar.gz"

View file

@ -29,6 +29,8 @@ def update_packages_config(conf_str):
_pkgx = (
"x",
"""\
from spack.package import *
class X(Package):
version("1.1")
version("1.0")
@ -45,6 +47,8 @@ class X(Package):
_pkgy = (
"y",
"""\
from spack.package import *
class Y(Package):
version("2.5")
version("2.4")
@ -59,6 +63,8 @@ class Y(Package):
_pkgv = (
"v",
"""\
from spack.package import *
class V(Package):
version("2.1")
version("2.0")
@ -69,6 +75,8 @@ class V(Package):
_pkgt = (
"t",
"""\
from spack.package import *
class T(Package):
version('2.1')
version('2.0')
@ -81,6 +89,8 @@ class T(Package):
_pkgu = (
"u",
"""\
from spack.package import *
class U(Package):
version('1.1')
version('1.0')

View file

@ -4,11 +4,13 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
import os
import re
import pytest
import spack.directives
import spack.fetch_strategy
import spack.package_base
import spack.repo
from spack.paths import mock_packages_path
from spack.spec import Spec
@ -318,3 +320,24 @@ def test_package_deprecated_version(mock_packages, mock_fetch, mock_stage):
assert spack.package_base.deprecated_version(pkg_cls, "1.1.0")
assert not spack.package_base.deprecated_version(pkg_cls, "1.0.0")
def test_package_with_deprecated_magic_import_has_a_useful_error(tmpdir, mutable_config):
"""Test that a package that's missing `from spack.package import *` gets a useful error,
suggesting that it be added."""
tmpdir.join("repo.yaml").write("repo:\n namespace: old_package")
tmpdir.join("packages", "old-package").ensure(dir=True).join("package.py").write(
"""\
class OldPackage(Package):
version('1.0', '0123456789abcdef0123456789abcdef')
"""
)
with spack.repo.use_repositories(str(tmpdir)) as repo:
with pytest.raises(
spack.repo.RepoError,
match=re.escape(
"This usually means `from spack.package import *` "
"is missing at the top of the package.py file."
),
):
repo.get_pkg_class("old-package")

View file

@ -1,3 +1,5 @@
from spack.package import *
class {{ cls_name }}(Package):
homepage = "http://www.example.com"
url = "http://www.example.com/root-1.0.tar.gz"