Skip to content
This repository was archived by the owner on Dec 16, 2022. It is now read-only.

Fix CLI and install instructions in case optional checklists is not present #5589

Merged
merged 15 commits into from
Mar 21, 2022
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ jobs:
strategy:
matrix:
python: ['3.7', '3.8', '3.9']
# check that CLI remains working for all package flavors;
# currently allennlp[checklist]==allennlp[all], so avoiding duplication on this
flavor: ['', '[all]']

steps:
- uses: actions/checkout@v2
Expand All @@ -369,7 +372,7 @@ jobs:

- name: Install core package
run: |
pip install $(ls dist/*.whl)[all]
pip install $(ls dist/*.whl)${{ matrix.flavor }}

- name: Download NLTK prerequisites
run: |
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Removed unnecessary dependencies
- Restore functionality of CLI in absence of now-optional checklist-package


## [v2.9.1](https://github.com/allenai/allennlp/releases/tag/v2.9.1) - 2022-03-09
Expand Down
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,19 @@ We support AllenNLP on Mac and Linux environments. We presently do not support W

### Installing via conda-forge

The simplest way to install AllenNLP is using conda:
The simplest way to install AllenNLP is using conda (you can choose a different python version):

```
conda install -c conda-forge python=3.8 allennlp
```

All plugins mentioned above are similarly installable, e.g.
To install optional packages, such as `checklist`, use

```
conda install -c conda-forge allennlp-checklist
```

or simply install `allennlp-all` directly. The plugins mentioned above are similarly installable, e.g.

```
conda install -c conda-forge allennlp-models allennlp-semparse allennlp-server allennlp-optuna
Expand All @@ -211,10 +217,10 @@ environment you want to use, you can skip to the 'installing via pip' section.

1. [Download and install Conda](https://conda.io/projects/conda/en/latest/user-guide/install/index.html).

2. Create a Conda environment with Python 3.7 (3.6 or 3.8 would work as well):
2. Create a Conda environment with Python 3.8 (3.7 or 3.9 would work as well):

```
conda create -n allennlp_env python=3.7
conda create -n allennlp_env python=3.8
```

3. Activate the Conda environment. You will need to activate the Conda environment in each terminal in which you want to use AllenNLP:
Expand Down
7 changes: 0 additions & 7 deletions allennlp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
# Make sure that allennlp is running on Python 3.6.1 or later
# (to avoid running into this bug: https://bugs.python.org/issue29246)
import sys

if sys.version_info < (3, 6, 1):
raise RuntimeError("AllenNLP requires Python 3.6.1 or later")

# We get a lot of these spurious warnings,
# see https://github.com/ContinuumIO/anaconda-issues/issues/6678
import warnings # noqa
Expand Down
12 changes: 1 addition & 11 deletions allennlp/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from allennlp import __version__
from allennlp.commands.build_vocab import BuildVocab
from allennlp.commands.cached_path import CachedPath
from allennlp.commands.checklist import CheckList
from allennlp.commands.diff import Diff
from allennlp.commands.evaluate import Evaluate
from allennlp.commands.find_learning_rate import FindLearningRate
Expand All @@ -22,17 +23,6 @@

logger = logging.getLogger(__name__)

try:
"""
The `allennlp checklist` command requires installation of the optional dependency `checklist`.
It can be installed with `pip install allennlp[checklist]`.
"""
with warnings.catch_warnings():
warnings.simplefilter("ignore")
from allennlp.commands.checklist import CheckList
except ImportError:
pass


class ArgumentParserWithDefaults(argparse.ArgumentParser):
"""
Expand Down
204 changes: 204 additions & 0 deletions allennlp/commands/_checklist_internal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
"""
The `checklist` subcommand allows you to conduct behavioural
testing for your model's predictions using a trained model and its
[`Predictor`](../predictors/predictor.md#predictor) wrapper.
"""
Comment on lines +1 to +5
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a case where git does a better job than the github UI; in particular, this is a 100% copy of the file as it existed before ec96bef:

[cf ec96bef1] create dummy command if checklist not available
 4 files changed, 51 insertions(+), 236 deletions(-)
 copy allennlp/commands/{checklist.py => _checklist_internal.py} (100%)
 rewrite allennlp/commands/checklist.py (94%)
 rewrite allennlp/confidence_checks/task_checklists/__init__.py (100%)


from typing import Optional, Dict, Any, List
import argparse
import sys
import json
import logging


from allennlp.commands.subcommand import Subcommand
from allennlp.common.checks import check_for_gpu, ConfigurationError
from allennlp.models.archival import load_archive
from allennlp.predictors.predictor import Predictor

logger = logging.getLogger(__name__)

try:
from allennlp.confidence_checks.task_checklists.task_suite import TaskSuite
except ImportError:
raise


@Subcommand.register("checklist")
class CheckList(Subcommand):
def add_subparser(self, parser: argparse._SubParsersAction) -> argparse.ArgumentParser:

description = """Run the specified model through a checklist suite."""
subparser = parser.add_parser(
self.name,
description=description,
help="Run a trained model through a checklist suite.",
)

subparser.add_argument(
"archive_file", type=str, help="The archived model to make predictions with"
)

subparser.add_argument("task", type=str, help="The name of the task suite")

subparser.add_argument("--checklist-suite", type=str, help="The checklist suite path")

subparser.add_argument(
"--capabilities",
nargs="+",
default=[],
help=('An optional list of strings of capabilities. Eg. "[Vocabulary, Robustness]"'),
)

subparser.add_argument(
"--max-examples",
type=int,
default=None,
help="Maximum number of examples to check per test.",
)

subparser.add_argument(
"--task-suite-args",
type=str,
default="",
help=(
"An optional JSON structure used to provide additional parameters to the task suite"
),
)

subparser.add_argument(
"--print-summary-args",
type=str,
default="",
help=(
"An optional JSON structure used to provide additional "
"parameters for printing test summary"
),
)

subparser.add_argument("--output-file", type=str, help="Path to output file")

subparser.add_argument(
"--cuda-device", type=int, default=-1, help="ID of GPU to use (if any)"
)

subparser.add_argument(
"--predictor", type=str, help="Optionally specify a specific predictor to use"
)

subparser.add_argument(
"--predictor-args",
type=str,
default="",
help=(
"An optional JSON structure used to provide additional parameters to the predictor"
),
)

subparser.set_defaults(func=_run_suite)

return subparser


def _get_predictor(args: argparse.Namespace) -> Predictor:
check_for_gpu(args.cuda_device)
archive = load_archive(
args.archive_file,
cuda_device=args.cuda_device,
)

predictor_args = args.predictor_args.strip()
if len(predictor_args) <= 0:
predictor_args = {}
else:
predictor_args = json.loads(predictor_args)

return Predictor.from_archive(
archive,
args.predictor,
extra_args=predictor_args,
)


def _get_task_suite(args: argparse.Namespace) -> TaskSuite:
available_tasks = TaskSuite.list_available()
if args.task in available_tasks:
suite_name = args.task
else:
raise ConfigurationError(
f"'{args.task}' is not a recognized task suite. "
f"Available tasks are: {available_tasks}."
)

file_path = args.checklist_suite

task_suite_args = args.task_suite_args.strip()
if len(task_suite_args) <= 0:
task_suite_args = {}
else:
task_suite_args = json.loads(task_suite_args)

return TaskSuite.constructor(
name=suite_name,
suite_file=file_path,
extra_args=task_suite_args,
)


class _CheckListManager:
def __init__(
self,
task_suite: TaskSuite,
predictor: Predictor,
capabilities: Optional[List[str]] = None,
max_examples: Optional[int] = None,
output_file: Optional[str] = None,
print_summary_args: Optional[Dict[str, Any]] = None,
) -> None:
self._task_suite = task_suite
self._predictor = predictor
self._capabilities = capabilities
self._max_examples = max_examples
self._output_file = None if output_file is None else open(output_file, "w")
self._print_summary_args = print_summary_args or {}

if capabilities:
self._print_summary_args["capabilities"] = capabilities

def run(self) -> None:
self._task_suite.run(
self._predictor, capabilities=self._capabilities, max_examples=self._max_examples
)

# We pass in an IO object.
output_file = self._output_file or sys.stdout
self._task_suite.summary(file=output_file, **self._print_summary_args)

# If `_output_file` was None, there would be nothing to close.
if self._output_file is not None:
self._output_file.close()


def _run_suite(args: argparse.Namespace) -> None:

task_suite = _get_task_suite(args)
predictor = _get_predictor(args)

print_summary_args = args.print_summary_args.strip()
if len(print_summary_args) <= 0:
print_summary_args = {}
else:
print_summary_args = json.loads(print_summary_args)

capabilities = args.capabilities
max_examples = args.max_examples

manager = _CheckListManager(
task_suite,
predictor,
capabilities,
max_examples,
args.output_file,
print_summary_args,
)
manager.run()
Loading