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
New spack.environment.active_environment
api, and make spack.environment not depend on spack.cmd.
#25439
Conversation
16f38aa
to
bea4caf
Compare
Added the |
bea4caf
to
57ea171
Compare
@gartung any idea why |
Stage directory was not removed spack/lib/spack/spack/test/conftest.py Line 291 in 3773185
|
Sure, but it's failing spuriously, any idea why? |
It's a test package so it creates it's own files on the fly. Maybe there is a timing issue with the cleanup of the stage directory. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't love the arrangement of methods in the cmd vs env modules, but I didn't love the old arrangement either so I'm not going to complain too much about that.
A couple minor issues.
lib/spack/spack/cmd/verify.py
Outdated
@@ -74,7 +73,7 @@ def verify(parser, args): | |||
|
|||
elif args.specs_or_files: | |||
# construct disambiguated spec list | |||
env = ev.get_env(args, 'verify') | |||
env = spack.environment.get_active_env() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to import spack.environment
at the top here, and this goes for most of the files where you deleted it.
The only reason it's working without is because the name gets bound from importing spack.store
, but you shouldn't rely on that because it's undefined python behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@haampie I think this might be related to the test failure -- you can get some really inscrutable errors with improper imports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm okay, let me try to fix the circular dependencies. Note that def matching_spec_from_env
in __init__.py
already used spack.environment.<stuff>
without importing before this pr.
But I mean, this PR is intended to separate core domain stuff (spack.environment) from the interface layer (spack.cmd), so optimally there would not be any circular deps at all...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of commits were necessary to fix circular imports, but then it's OK to add those spack.environment
imports in spack.cmd.*. I've done it over the whole code base in 1ac0eb3, and made it consistently use import spack.environment as ev
since this was already done in 55 out of 62 cases.
48f1d83
to
117579a
Compare
45699cb
to
84f57de
Compare
bec2625
to
f9d0218
Compare
- Make `spack.environment.get_env` a trivial getter for the active environment. - Introduce `spack.cmd.get_env_for_command(cmd_name, env)` which is a wrapper around spack.env.get_env(env)` but adds support for an error message for the particular command. - Clean up instances where `spack.environment.get_env` was abused outside the context of a command (fake command name, bunch of None arguments which are hard to follow) - Remove the `-e` parsing from `get_env`, because `main.py` is responsible for processing `-e` and already activates the environment. - Move `spack.environment.find_environment` to `spack.cmd.find_environment`, to avoid that spack.environment is aware of argparse.
The way I see it update_kwargs_from_args could have been a constructor for a PackageInstallArgs object, and they could have a named constructor like PackageInstallArgs::from_cli_args(args). So it makes sense to move this function to spack.installer, and with that the last dependency of spack.environment on spack.cmd is gone, as it should be.
f9d0218
to
8917731
Compare
@spackbot run pipeline |
I've started that pipeline for you! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a few comments. Overall I think this is an improvement over current state, and it catches a few places where we were missing imports. There's a function that I think is being moved to the wrong module and a few local imports that I would avoid if possible.
Style question to everyone. Do we want to consistently:
import spack.environment as ev
everywhere? I have a slight preference for fully qualified names, but if we go with an alias everywhere I'm fine with it. It would be good if we can decide on that and have the decision checked automatically by style tests, if possible and easy to do.
lib/spack/spack/installer.py
Outdated
@@ -613,6 +613,42 @@ def package_id(pkg): | |||
return "{0}-{1}-{2}".format(pkg.name, pkg.version, pkg.spec.dag_hash()) | |||
|
|||
|
|||
def update_kwargs_from_args(args, kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like this is the wrong place for this function if the aim was to have the spack core module not dependent on the cli. Here we assume knowledge on which arguments are passed in an argparse
namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I tried to explain this in the commit message 8917731:
The way I see it update_kwargs_from_args could have been a constructor
for a PackageInstallArgs object, and they could have a named constructor
like PackageInstallArgs::from_cli_args(args). So it makes sense to move
this function to spack.installer, and with that the last dependency of
spack.environment on spack.cmd is gone, as it should be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could move it out of installer.py, but I don't know where it should go instead 🤔 installer_args.py?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I see that commit. My opinion is that the right solution is to have:
spack/lib/spack/spack/environment.py
Line 1575 in b22728d
def install_specs(self, specs=None, args=None, **install_args): |
and similar functions not having the args
argument. It should be on the caller of the interface to prepare the install_args
appropriately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at:
spack/lib/spack/spack/cmd/install.py
Line 197 in b22728d
def install_specs(cli_args, kwargs, specs): |
it seems this is doable. Besides, update_kwargs_from_args
is called internally from Environment.install_specs
which receives directly the cli_args
and externally in the function above. So why not setting everything in cmd/install.py
and just pass a dictionary after a single call to the function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've removed args
from spack.environment.install_spec(...)
and spack.environment.install_all(...)
and combined the update_kwargs_from_args
in spack.cmd.install
. That seems like a better solution indeed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's exactly what I had in mind. Thanks!
@@ -7,7 +7,7 @@ | |||
import pytest | |||
|
|||
import spack.config | |||
import spack.environment | |||
import spack.environment as ev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #25439 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can take care of understanding these two failures in a following PR.
lib/spack/spack/concretize.py
Outdated
@@ -63,10 +63,11 @@ def __init__(self, abstract_spec=None): | |||
self._adjust_target_answer_generator = None | |||
|
|||
def concretize_develop(self, spec): | |||
import spack.environment as ev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why doing a local import rather than one at the top of the module?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because of Python 2:
$ SPACK_PYTHON=python2 spack --version
Traceback (most recent call last):
File "/home/harmen/spack/bin/spack", line 96, in <module>
import spack.main # noqa
File "/home/harmen/spack/lib/spack/spack/main.py", line 36, in <module>
import spack.cmd
File "/home/harmen/spack/lib/spack/spack/cmd/__init__.py", line 24, in <module>
import spack.environment as ev
File "/home/harmen/spack/lib/spack/spack/environment.py", line 20, in <module>
import spack.concretize
File "/home/harmen/spack/lib/spack/spack/concretize.py", line 36, in <module>
import spack.environment as ev
AttributeError: 'module' object has no attribute 'environment'
There's a circularity between environment & concretize, but it's only an error on Python 2.7, not Python 3.x.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually... when I drop as ev
here, Python 2 does not complain 😕.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you open an issue as soon as we merge this, to understand what's going on?
My reason for it was that we're doing it already in 88% of the cases: $ git checkout develop
$ grep '--include=*.py' 'import spack.environment as ev' -r lib/ | wc -l
55
$ grep '--include=*.py' 'import spack.environment' -r lib/ | wc -l
62 Edit: turns out python 2.7 struggles with alias'ed imports 6b8e412, so |
Apparently no longer necessary to have local imports because of later changes
The error message "'module' object has no attribute 'environment'" is gone after using `import spack.environment` without an alias on Python 2.7, so these are the only exceptions to `as ev` everywhere...
This reverts commit 8917731.
c9f4ad2
to
8a4d6e7
Compare
I think I've incorporated all the review comments now, and I've updated the PR description at the top with a summary of the changes. |
spack.environment.active_environment
api, and make spack.environment not depend on spack.cmd.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -324,10 +324,14 @@ def get_tests(specs): | |||
else: | |||
return False | |||
|
|||
# Parse cli arguments and construct a dictionary | |||
# that will be passed to the package installer | |||
update_kwargs_from_args(args, kwargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks much better to me now!
|
||
def install_specs(self, specs=None, **install_args): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely a better API imo without a dependency on an argparse
namespace. Thanks for taking care of that!
@@ -7,7 +7,7 @@ | |||
import pytest | |||
|
|||
import spack.config | |||
import spack.environment | |||
import spack.environment as ev |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can take care of understanding these two failures in a following PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly looks good, I agree the changes you made after Max's review are great improvements. only minor stuff left.
lib/spack/spack/util/__init__.py
Outdated
@@ -2,3 +2,19 @@ | |||
# Spack Project Developers. See the top-level COPYRIGHT file for details. | |||
# | |||
# SPDX-License-Identifier: (Apache-2.0 OR MIT) | |||
|
|||
|
|||
def elide_list(line_list, max_num=10): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This probably belongs in llnl.util.lang (the same module as dedupe
and star
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -501,3 +486,71 @@ def extant_file(f): | |||
if not os.path.isfile(f): | |||
raise argparse.ArgumentTypeError('%s does not exist' % f) | |||
return f | |||
|
|||
|
|||
def require_active_env(cmd_name): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't fully understand why this method would be in spack.cmd... does that help with the circular import problem if you put it in spack.environment
(and leave find_environment
here because it uses argparse)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's in spack.cmd
because its only purpose is to create an error that shows you how to use the command if you didn't activate an env.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm still not convinced on whether the methods are all in the perfect modules, but I don't think we should hold this up over it.
Thanks! |
spack.environment.get_env(...)
tospack.environment.active_environment()
which is now a trivial getter for the active environment.
spack.cmd.require_active_env(cmd_name=...)
which is a wrapperaround
spack.environment.active_environment()
but errors with a helpfulerror message when used from the CLI.
spack.environment.get_env
was abusedoutside the context of a command (fake command name, fake
-e
flag,bunch of
None
,None
arguments)-e
parsing from formerget_env
, becausemain.py
isresponsible for processing
-e
and already activates the environment.In fact this logic was entirely broken, because it did not respect
-D
at allfor loading an env from a directory, and it did not respect
-E
(i.e. it wouldload an environment even when explicitly disabled).
spack.environment.find_environment
tospack.cmd.find_environment
, to avoid that spack.environment is awareof argparse.
installs_specs
fromspack.environment
tospack.cmd
, to avoid that spack.environment is awareof argparse.
ev
alias forspack.environment
consistently where it works Python 2.7permitting
TL;DR: better interface for getting the currently active environment, and refactor
things so that
spack.environment
does not depend onspack.cmd
.