Repository: quantumlib/Cirq

Issue:
Push to PyPi failing
```
error in cirq setup command: 'extras_require' must be a dictionary whose values are strings or lists of strings containing valid project/version requirement specifiers.
```

See https://github.com/quantumlib/Cirq/runs/2851981344



Discussion:

Resolving patch:
diff --git a/dev_tools/modules.py b/dev_tools/modules.py
--- a/dev_tools/modules.py
+++ b/dev_tools/modules.py
@@ -27,8 +27,10 @@
 optional arguments:
   -h, --help            show this help message and exit
   --mode {folder,package-path}
-                        'folder' to list root folder for module, 'package-path' for top level
-                        python package path
+                        'folder' to list root folder for module (e.g. cirq-google),
+                        'package-path' for top level python package path
+                            (e.g. cirq-google/cirq_google),
+                        'package' for top level python package (e.g cirq_google),
   --include-parent      whether to include the parent package or not
 """
 
@@ -41,6 +43,7 @@
 
 _FOLDER = 'folder'
 _PACKAGE_PATH = 'package-path'
+_PACKAGE = 'package'
 
 
 @dataclasses.dataclass
@@ -142,6 +145,9 @@ def _print_list_modules(mode: str, include_parent: bool = False):
         elif mode == _PACKAGE_PATH:
             for p in m.top_level_package_paths:
                 print(p, end=" ")
+        elif mode == _PACKAGE:
+            for package in m.top_level_packages:
+                print(package, end=" ")
 
 
 def main(argv: List[str]):
@@ -169,10 +175,11 @@ def _add_list_modules_cmd(subparsers):
     list_modules_cmd.add_argument(
         "--mode",
         default=_FOLDER,
-        choices=[_FOLDER, _PACKAGE_PATH],
+        choices=[_FOLDER, _PACKAGE_PATH, _PACKAGE],
         type=str,
-        help="'folder' to list root folder for module,\n"
-        "'package-path' for top level python package path",
+        help="'folder' to list root folder for module (e.g. cirq-google),\n"
+        "'package-path' for top level python package path (e.g. cirq-google/cirq_google),\n"
+        "'package' for top level python package (e.g cirq_google),\n",
     )
     list_modules_cmd.add_argument(
         "--include-parent",
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -62,7 +62,7 @@
     url='http://github.com/quantumlib/cirq',
     author='The Cirq Developers',
     author_email='cirq-dev@googlegroups.com',
-    python_requires=('>=3.6.0'),
+    python_requires='>=3.6.0',
     install_requires=requirements,
     extras_require={
         'dev_env': dev_requirements,


Test patch:
diff --git a/dev_tools/modules_test.py b/dev_tools/modules_test.py
--- a/dev_tools/modules_test.py
+++ b/dev_tools/modules_test.py
@@ -113,6 +113,10 @@ def test_main():
         modules.main(["list", "--mode", "folder", "--include-parent"])
         assert output.getvalue() == ' '.join(["mod1", "mod2", ".", ""])
 
+    with mock.patch('sys.stdout', new=StringIO()) as output:
+        modules.main(["list", "--mode", "package"])
+        assert output.getvalue() == ' '.join(["pack1", "pack2", ""])
+
 
 @chdir(target_dir=None)
 def test_error():
diff --git a/dev_tools/packaging/packaging_test.sh b/dev_tools/packaging/packaging_test.sh
new file mode 100755
--- /dev/null
+++ b/dev_tools/packaging/packaging_test.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+# Copyright 2021 The Cirq Developers
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+################################################################################
+# This script tests packaging. It creates the packages for all the cirq modules
+# `pip install`s them in a clean virtual environment and then runs some simple
+# verificiations on each of the modules, ensuring that they can be imported.
+################################################################################
+
+set -e
+
+# Temporary workspace.
+tmp_dir=$(mktemp -d)
+trap "{ rm -rf ${tmp_dir}; }" EXIT
+
+# New virtual environment
+echo "Working in a fresh virtualenv at ${tmp_dir}/env"
+virtualenv --quiet "--python=/usr/bin/python3" "${tmp_dir}/env"
+
+export CIRQ_PRE_RELEASE_VERSION=$(dev_tools/packaging/generate-dev-version-id.sh)
+out_dir=${tmp_dir}/dist
+dev_tools/packaging/produce-package.sh ${out_dir} $CIRQ_PRE_RELEASE_VERSION
+
+# test installation 
+"${tmp_dir}/env/bin/python" -m pip install ${out_dir}/*
+
+echo ===========================
+echo Testing that code executes
+echo ===========================
+
+"${tmp_dir}/env/bin/python" -c "import cirq; print(cirq.google.Foxtail)"
+"${tmp_dir}/env/bin/python" -c "import cirq_google; print(cirq_google.Foxtail)"
+"${tmp_dir}/env/bin/python" -c "import cirq; print(cirq.Circuit(cirq.CZ(*cirq.LineQubit.range(2))))"
+
+echo =======================================
+echo Testing that all modules are installed
+echo =======================================
+
+CIRQ_PACKAGES=$(env PYTHONPATH=. python dev_tools/modules.py list --mode package)
+for p in $CIRQ_PACKAGES; do
+  echo --- Testing $p -----
+  python_test="import $p; print($p); assert '${tmp_dir}' in $p.__file__, 'Package path seems invalid.'"
+  env PYTHONPATH='' "${tmp_dir}/env/bin/python" -c "$python_test" && echo -e "\033[32mPASS\033[0m"  || echo -e "\033[31mFAIL\033[0m"
+done
\ No newline at end of file