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

DM-25385 make a pipetask command based on click #318

Merged
merged 25 commits into from Jul 15, 2020
Merged

Conversation

n8pease
Copy link
Contributor

@n8pease n8pease commented Jun 21, 2020

No description provided.

Copy link
Member

@timj timj left a comment

Choose a reason for hiding this comment

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

Looks okay. A comment in the pull request description providing an overview of the changes would have helped me a little since the PR title doesn't help (I really need to take another look at that dev guide rule) and the commit messages are very terse. I think what you've done here is rearrange the code a bit into a class so that the pipetask infrastructure can use it.

Copy link
Contributor

@andy-slac andy-slac left a comment

Choose a reason for hiding this comment

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

Looks OK, few minor comments.


def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class LoaderCLI(click.MultiCommand, abc.ABC):
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it needs docstring, it's hard to guess what its purpose is.

"""Convert function name to the butler command name: change
underscores, (used in functions) to dashes (used in commands), and
change local-package command names that conflict with python keywords
to a leagal function name.
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: leagal

@classmethod
def _funcNameToCmdName(cls, functionName):
"""Convert function name to the butler command name: change
underscores, (used in functions) to dashes (used in commands), and
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for comma after "underscores"?

"""Convert function name to the butler command name: change
underscores, (used in functions) to dashes (used in commands), and
change local-package command names that conflict with python keywords
to a leagal function name.
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you need to repeat whole docstring for the method that you are overriding, just add a comment like this:
https://github.com/lsst/daf_butler/blob/master/python/lsst/daf/butler/datastores/chainedDatastore.py#L354

required : bool, optional
If true, the option is required to be passed in on the command line, by
default False
split_kv_sep : `str` or None, optional
Copy link
Contributor

Choose a reason for hiding this comment

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

There is split_kv parameter but no split_kv_sep.


Returns
-------
cli : a click.Command function.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is "a click.Command function" different from `click.Command`?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case click.Command is more correct. By "command function" I mean the function decorated by @click.Command, whereas a click.Command instance would wrap & contain a command function.

invoker.
"""
cliArgs = copy.copy(parameterKwargs) if parameterKwargs is not None else {}
if 'parameterType' not in cliArgs and optTestBase.isArgument and optTestBase.isParameter:
Copy link
Contributor

Choose a reason for hiding this comment

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

Strings need double quotes (many other places too).

@abc.abstractmethod
def optionName(self):
"""The option name, matches the option flag that appears on the command
line
Copy link
Contributor

Choose a reason for hiding this comment

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

Need more periods everywhere at the end of sentences.

if testObj.valueType.dir_okay:
os.makedirs(testObj.optionValue)
elif testObj.valueType.file_okay:
_ = open(testObj.optionValue)
Copy link
Contributor

Choose a reason for hiding this comment

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

My guess is that you are creating a file here, should this be open(..., "w")? And I think it needs an explicit close() or with open(...).

@@ -55,9 +55,9 @@ def test_requiredMissing(self):
def test_all(self):
"""Test all parameters."""
self.run_test(["query-dataset-types", "here", "--verbose", "foo*", "--components"],
self.makeExpected(repo="here", verbose=True, glob=("foo*", ), components=True))
self.makeExpected(repo="here", verbose=True, glob=["foo*", ], components=True))
Copy link
Contributor

Choose a reason for hiding this comment

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

Can remove comma too to save two characters 🙂 (and below too)

@n8pease n8pease force-pushed the tickets/DM-25385 branch 3 times, most recently from a57885e to ca0d8a9 Compare July 2, 2020 20:33

# Forward all Python logging to lsstLog
lgr.addHandler(lsstLog.LogHandler())
lgr.setLevel(logging.INFO)
Copy link
Contributor

Choose a reason for hiding this comment

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

If lsstLog is None then I think we probably need to call logging.basicConfig(level=INFO) instead of just lgr.setLevel(logging.INFO).

def uninitLog(cls, components):
if lsstLog is not None:
lgr = logging.getLogger()
lgr.removeHandler(lsstLog.LogHandler())
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm 🤔 I'm not sure it is going to work. Do you have unit test for this?

Copy link
Contributor Author

@n8pease n8pease Jul 7, 2020

Choose a reason for hiding this comment

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

@andy-slac can you say more about what part won't work? What would you like tested?

Copy link
Contributor

Choose a reason for hiding this comment

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

What I think is that removeHandler removes existing handler from the the handler list. lsstLog.LogHandler() make a new handler that was never added to that list, so removing it does not even make sense (it is probably no-op). What you want is to remove handler instance that you created earlier and added with addHandler , this means you need to remember that instance somewhere. As for testing, I think you can check the size of the lgr.handlers to see if removing changes its size (though handlers is an undocumented attribute)

Copy link
Member

Choose a reason for hiding this comment

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

You can't test in daf_butler itself because daf_butler doesn't depend on lsst.log.

Copy link
Contributor

Choose a reason for hiding this comment

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

You can if you (accidentally) setup log or some other package that depends on log.

Copy link
Member

Choose a reason for hiding this comment

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

That is true but you can't rely on it from a Jenkins point of view. ctrl_mpexec can test it though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have tests in obs_base for features in daf_butler that depend on lsst.log. Let me know if you think that's not a good place.
I don't think I can depend on the actual number of handlers, tests may run before this test may affect it. I'm not even sure I can depend on a delta. I might have to figure out how to tell if a specific handler is in the list, or punt on being able to know.
The affect that persistent logging configuration has on unrelated unit tests that run in the same process is really giving me hell. I might have questions in Slack today.

Copy link
Member

Choose a reason for hiding this comment

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

obs_base is good. Logging does affect global state which is why logger configurations should only be triggered by command line tooling and not by module code.


Parameters
----------
longlog : `bool`
Copy link
Contributor

Choose a reason for hiding this comment

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

NSP - no such parameter :)

logLevels = logLevels.items()

# configure individual loggers
for component, level in logLevels:
Copy link
Contributor

Choose a reason for hiding this comment

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

DO you need to do level = level.upper()?

@n8pease n8pease force-pushed the tickets/DM-25385 branch 2 times, most recently from ea759ef to 23701d3 Compare July 15, 2020 16:32
This allows the LoaderCLI class to be used for the butler command
as well as other commands (currently implementing `pipetask`).
The traceback helps with debugging unit tests.
contians fixes found during conversion
Init'ing logging before trying to get commands or a command allows
logging info about errors encountered during those actions.
when an option that accepts a key-value input that does not
accept multiple values gets a comma-separated value, give an
accurate error report.
This helps with click help text when it's long, because the
formatting preserved in the triple-quoted string conflicts with
the line wrapping in click's help text output.
Allows commands other than 'butler' to be tested (currently
working on 'pipetask').
The name of the environment variable that caused the butler
command to call a mock instead of a script (command implementation)
function was butler-specific. Renaming to be more correct when
mocking commands other than butler (currently working on pipetask).
Add MWOption and MWArgument (MW = "middleware") for metavar
formatting, can be used for other parameter overrides as needed.
The override added in this commit makes sure that the " ..."
after the type metavar is present in all the cases where
multiple arguments are accepted.
* change log-level to optionally accept key-value pairs
* configure python and C++ logging
* init (add handler) to the log once
* log level can be set on modules or globally at any time
* add a logging unit test

Changes the type passed from the log-level option log_level
to be a dict, any non-kv value is put in the dict in an
entry whose key is an empty string. If any keys are
repeated (including no key, in which case the key will be
an empty string) then the last entry takes precedence.

Applies the Choice (accepted input values) to the value;
any key will be accepted.

add logging unit test
use mock to verify expected call instead of printing to and
reading from stdout.

also use setUp to create the click test runner.
This context manager helps tests publish command plugins temporarily.
This allows click's CliRunner in unit tests to execute commands that affect
the log settings, and undoes those settings so that the logging
system is not left in an unexpected state after running CLI unit
tests in the same process as other unit tests.
test_cliOption.py was added to remove redundancy in option tests,
factor test_cliArgumentRepo.py into test_cliOption to take advantage
of all the test reuse there.
create is covered in test_cliCmdCreate.py
test_cliOption.py was added to remove redundancy in option tests,
factor test_cliOptionTransfer.py into test_cliOption to take advantage
of all the test reuse there.
LogCliRunner extends CliRunner to reset the CliLog at the end of
invoke.
@n8pease n8pease merged commit 64019ca into master Jul 15, 2020
@timj timj deleted the tickets/DM-25385 branch February 16, 2024 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants