We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Conda should automatically test imports of py_modules as well as packages
Should be pretty simple to add.

I'd make a PR myself but I haven't been able to make a dev install for conda-build yet (#141).

More context: https://github.com/conda/conda-build/issues/139#issuecomment-49466828


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `bdist_conda.py`
Content:
```
1 """
2 bdist_conda
3 
4 """
5 from __future__ import (print_function, division, unicode_literals,
6     absolute_import)
7 
8 from collections import defaultdict
9 
10 from distutils.command.install import install
11 from distutils.errors import DistutilsOptionError, DistutilsGetoptError
12 from distutils.dist import Distribution
13 
14 from conda.compat import (StringIO, string_types, configparser, PY3, text_type
15 as unicode)
16 from conda.lock import Locked
17 import conda.config
18 from conda.cli.common import spec_from_line
19 from conda_build.metadata import MetaData
20 from conda_build import config, build, pypi
21 from conda_build.main_build import handle_binstar_upload
22 
23 # TODO: Add support for all the options that conda build has
24 
25 class CondaDistribution(Distribution):
26     """
27     Distribution subclass that supports bdist_conda options
28 
29     This class is required if you want to pass any bdist_conda specific
30     options to setup().  To use, set distclass=CondaDistribution in setup().
31 
32     **NOTE**: If you use setuptools, you must import setuptools before
33     importing distutils.commands.bdist_conda.
34 
35     Options that can be passed to setup() (must include
36     distclass=CondaDistribution):
37 
38     - conda_buildnum: The build number. Defaults to 0. Can be overridden on
39       the command line with the --buildnum flag.
40 
41     - conda_buildstr: The build string. Default is generated automatically
42       from the Python version, NumPy version if relevant, and the build
43       number, like py34_0.
44 
45     - conda_import_tests: Whether to automatically run import tests. The
46       default is True, which runs import tests for the all the modules in
47       "packages". Also allowed are False, which runs no tests, or a list of
48       module names to be tested on import.
49 
50     - conda_command_tests: Command line tests to run. Default is True, which
51       runs ``command --help`` for each ``command`` in the console_scripts and
52       gui_scripts entry_points. Also allowed are False, which doesn't run any
53       command tests, or a list of command tests to run.
54 
55     - conda_binary_relocation: Whether binary files should be made relocatable
56       (using install_name_tool on OS X or patchelf on Linux). The default is
57       True. See the "making packages relocatable" section in the conda build
58       documentation for more information on this.
59 
60     - conda_preserve_egg_dir: Whether to preserve the egg directory as
61       installed by setuptools.  The default is True if the package depends on
62       setuptools or has a setuptools entry_points other than console_scripts
63       and gui_scripts.
64 
65     - conda_features: List of features for the package. See the features
66       section of the conda build documentation for more information about
67       features in conda.
68 
69     - conda_track_features: List of features that this package should track
70       (enable when installed).  See the features section of the conda build
71       documentation for more information about features in conda.
72 
73     Command line options:
74 
75     --buildnum: Set the build number. Defaults to the conda_buildnum passed to
76       setup(), or 0. Overrides any conda_buildnum passed to setup().
77 
78     """
79     # Unfortunately, there's no way to warn the users that they need to use
80     # distclass=CondaDistribution when they try to use a conda option to
81     # setup(). Distribution.__init__ will just print a warning when it sees an
82     # attr it doesn't recognize, and then it is discarded.
83 
84     # attr: default
85     conda_attrs = {
86         'conda_buildnum': 0,
87         'conda_buildstr': None,
88         'conda_import_tests': True,
89         'conda_command_tests': True,
90         'conda_binary_relocation': True,
91         'conda_preserve_egg_dir': None,
92         'conda_features': None,
93         'conda_track_features': None,
94         }
95 
96     def __init__(self, attrs=None):
97         given_attrs = {}
98         # We need to remove the attrs so that Distribution.__init__ doesn't
99         # warn about them.
100         if attrs:
101             for attr in self.conda_attrs:
102                 if attr in attrs:
103                     given_attrs[attr] = attrs.pop(attr)
104 
105         if not PY3:
106             # Distribution is an old-style class in Python 3
107             Distribution.__init__(self, attrs)
108         else:
109             super().__init__(attrs)
110 
111         for attr in self.conda_attrs:
112             setattr(self.metadata, attr, given_attrs.get(attr, self.conda_attrs[attr]))
113 
114 class bdist_conda(install):
115     description = "create a conda package"
116 
117     def initialize_options(self):
118         if not PY3:
119             # Command is an old-style class in Python 2
120             install.initialize_options(self)
121         else:
122             super().initialize_options()
123         self.buildnum = None
124         self.binstar_upload = False
125 
126     def finalize_options(self):
127         opt_dict = self.distribution.get_option_dict('install')
128         if self.prefix:
129             raise DistutilsOptionError("--prefix is not allowed")
130         opt_dict['prefix'] = ("bdist_conda", config.build_prefix)
131         if not PY3:
132             # Command is an old-style class in Python 2
133             install.finalize_options(self)
134         else:
135             super().finalize_options()
136 
137     def run(self):
138         # Make sure the metadata has the conda attributes, even if the
139         # distclass isn't CondaDistribution. We primarily do this to simplify
140         # the code below.
141 
142         metadata = self.distribution.metadata
143 
144         for attr in CondaDistribution.conda_attrs:
145             if not hasattr(metadata, attr):
146                 setattr(metadata, attr,
147                     CondaDistribution.conda_attrs[attr])
148 
149         # The command line takes precedence
150         if self.buildnum is not None:
151             metadata.conda_buildnum = self.buildnum
152 
153         with Locked(config.croot):
154             d = defaultdict(dict)
155             # Needs to be lowercase
156             d['package']['name'] = metadata.name
157             d['package']['version'] = metadata.version
158             d['build']['number'] = metadata.conda_buildnum
159 
160             # MetaData does the auto stuff if the build string is None
161             d['build']['string'] = metadata.conda_buildstr
162 
163             d['build']['binary_relocation'] = metadata.conda_binary_relocation
164             d['build']['preserve_egg_dir'] = metadata.conda_preserve_egg_dir
165             d['build']['features'] = metadata.conda_features
166             d['build']['track_features'] = metadata.conda_track_features
167 
168             # XXX: I'm not really sure if it is correct to combine requires
169             # and install_requires
170             d['requirements']['run'] = d['requirements']['build'] = \
171                 [spec_from_line(i) for i in
172                     (metadata.requires or []) +
173                     (getattr(self.distribution, 'install_requires', []) or
174                         [])] + ['python']
175             if hasattr(self.distribution, 'tests_require'):
176                 # A lot of packages use extras_require['test'], but
177                 # tests_require is the one that is officially supported by
178                 # setuptools.
179                 d['test']['requires'] = [spec_from_line(i) for i in
180                     self.distribution.tests_require or []]
181 
182             d['about']['home'] = metadata.url
183             # Don't worry about classifiers. This isn't skeleton pypi. We
184             # don't need to make this work with random stuff in the wild. If
185             # someone writes their setup.py wrong and this doesn't work, it's
186             # their fault.
187             d['about']['license'] = metadata.license
188             d['about']['summary'] = self.distribution.description
189 
190             # This is similar logic from conda skeleton pypi
191             entry_points = getattr(self.distribution, 'entry_points', [])
192             if entry_points:
193                 if isinstance(entry_points, string_types):
194                     # makes sure it is left-shifted
195                     newstr = "\n".join(x.strip() for x in
196                         entry_points.split('\n'))
197                     c = configparser.ConfigParser()
198                     entry_points = {}
199                     try:
200                         c.readfp(StringIO(newstr))
201                     except Exception as err:
202                         # This seems to be the best error here
203                         raise DistutilsGetoptError("ERROR: entry-points not understood: " + str(err) + "\nThe string was" + newstr)
204                     else:
205                         for section in config.sections():
206                             if section in ['console_scripts', 'gui_scripts']:
207                                 value = ['%s=%s' % (option, config.get(section, option))
208                                          for option in config.options(section)]
209                                 entry_points[section] = value
210                             else:
211                                 # Make sure setuptools is added as a dependency below
212                                 entry_points[section] = None
213 
214                 if not isinstance(entry_points, dict):
215                     raise DistutilsGetoptError("ERROR: Could not add entry points. They were:\n" + entry_points)
216                 else:
217                     cs = entry_points.get('console_scripts', [])
218                     gs = entry_points.get('gui_scripts', [])
219                     # We have *other* kinds of entry-points so we need
220                     # setuptools at run-time
221                     if not cs and not gs and len(entry_points) > 1:
222                         d['requirements']['run'].append('setuptools')
223                         d['requirements']['build'].append('setuptools')
224                     entry_list = cs + gs
225                     if gs and conda.config.platform == 'osx':
226                         d['build']['osx_is_app'] = True
227                     if len(cs + gs) != 0:
228                         d['build']['entry_points'] = entry_list
229                         if metadata.conda_command_tests is True:
230                             d['test']['commands'] = list(map(unicode, pypi.make_entry_tests(entry_list)))
231 
232             if 'setuptools' in d['requirements']['run']:
233                 d['build']['preserve_egg_dir'] = True
234 
235 
236             if metadata.conda_import_tests:
237                 if metadata.conda_import_tests is True:
238                     if self.distribution.packages:
239                         d['test']['imports'] = self.distribution.packages
240                 else:
241                     d['test']['imports'] = metadata.conda_import_tests
242 
243             if (metadata.conda_command_tests and not
244                 isinstance(metadata.conda_command_tests,
245                 bool)):
246                 d['test']['commands'] = list(map(unicode, metadata.conda_command_tests))
247 
248             d = dict(d)
249             m = MetaData.fromdict(d)
250             # Shouldn't fail, but do you really trust the code above?
251             m.check_fields()
252             build.build(m, post=False)
253             # Do the install
254             if not PY3:
255                 # Command is an old-style class in Python 2
256                 install.run(self)
257             else:
258                 super().run()
259             build.build(m, post=True)
260             build.test(m)
261             if self.binstar_upload:
262                 class args:
263                     binstar_upload = self.binstar_upload
264                 handle_binstar_upload(build.bldpkg_path(m), args)
265             else:
266                 no_upload_message = """\
267 # If you want to upload this package to binstar.org later, type:
268 #
269 # $ binstar upload %s
270 """ % build.bldpkg_path(m)
271                 print(no_upload_message)
272 
273 
274 # Distutils looks for user_options on the class (not instance).  It also
275 # requires that it is an instance of list. So we do this here because we want
276 # to keep the options from the superclass (and because I don't feel like
277 # making a metaclass just to make this work).
278 
279 # We have to call str() on the options because our import of
280 # __future__.unicode_literals breaks distutils.fancy_getopt in Python 2.
281 bdist_conda.user_options.extend([
282     (str('buildnum='), None, str('''The build number of
283     the conda package. Defaults to 0, or the conda_buildnum specified in the
284     setup() function. The command line flag overrides the option to
285     setup().''')),
286     (str('binstar-upload'), None, ("""Upload the finished package to binstar""")),
287     ])
288 
289 bdist_conda.boolean_options.extend([str('binstar-upload')])
290
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/bdist_conda.py b/bdist_conda.py
--- a/bdist_conda.py
+++ b/bdist_conda.py
@@ -232,11 +232,10 @@
             if 'setuptools' in d['requirements']['run']:
                 d['build']['preserve_egg_dir'] = True
 
-
             if metadata.conda_import_tests:
                 if metadata.conda_import_tests is True:
-                    if self.distribution.packages:
-                        d['test']['imports'] = self.distribution.packages
+                    d['test']['imports'] = ((self.distribution.packages or [])
+                                            + (self.distribution.py_modules or []))
                 else:
                     d['test']['imports'] = metadata.conda_import_tests

{"golden_diff": "diff --git a/bdist_conda.py b/bdist_conda.py\n--- a/bdist_conda.py\n+++ b/bdist_conda.py\n@@ -232,11 +232,10 @@\n             if 'setuptools' in d['requirements']['run']:\n                 d['build']['preserve_egg_dir'] = True\n \n-\n             if metadata.conda_import_tests:\n                 if metadata.conda_import_tests is True:\n-                    if self.distribution.packages:\n-                        d['test']['imports'] = self.distribution.packages\n+                    d['test']['imports'] = ((self.distribution.packages or [])\n+                                            + (self.distribution.py_modules or []))\n                 else:\n                     d['test']['imports'] = metadata.conda_import_tests\n", "issue": "Conda should automatically test imports of py_modules as well as packages\nShould be pretty simple to add.\n\nI'd make a PR myself but I haven't been able to make a dev install for conda-build yet (#141).\n\nMore context: https://github.com/conda/conda-build/issues/139#issuecomment-49466828\n\n", "before_files": [{"content": "\"\"\"\nbdist_conda\n\n\"\"\"\nfrom __future__ import (print_function, division, unicode_literals,\n    absolute_import)\n\nfrom collections import defaultdict\n\nfrom distutils.command.install import install\nfrom distutils.errors import DistutilsOptionError, DistutilsGetoptError\nfrom distutils.dist import Distribution\n\nfrom conda.compat import (StringIO, string_types, configparser, PY3, text_type\nas unicode)\nfrom conda.lock import Locked\nimport conda.config\nfrom conda.cli.common import spec_from_line\nfrom conda_build.metadata import MetaData\nfrom conda_build import config, build, pypi\nfrom conda_build.main_build import handle_binstar_upload\n\n# TODO: Add support for all the options that conda build has\n\nclass CondaDistribution(Distribution):\n    \"\"\"\n    Distribution subclass that supports bdist_conda options\n\n    This class is required if you want to pass any bdist_conda specific\n    options to setup().  To use, set distclass=CondaDistribution in setup().\n\n    **NOTE**: If you use setuptools, you must import setuptools before\n    importing distutils.commands.bdist_conda.\n\n    Options that can be passed to setup() (must include\n    distclass=CondaDistribution):\n\n    - conda_buildnum: The build number. Defaults to 0. Can be overridden on\n      the command line with the --buildnum flag.\n\n    - conda_buildstr: The build string. Default is generated automatically\n      from the Python version, NumPy version if relevant, and the build\n      number, like py34_0.\n\n    - conda_import_tests: Whether to automatically run import tests. The\n      default is True, which runs import tests for the all the modules in\n      \"packages\". Also allowed are False, which runs no tests, or a list of\n      module names to be tested on import.\n\n    - conda_command_tests: Command line tests to run. Default is True, which\n      runs ``command --help`` for each ``command`` in the console_scripts and\n      gui_scripts entry_points. Also allowed are False, which doesn't run any\n      command tests, or a list of command tests to run.\n\n    - conda_binary_relocation: Whether binary files should be made relocatable\n      (using install_name_tool on OS X or patchelf on Linux). The default is\n      True. See the \"making packages relocatable\" section in the conda build\n      documentation for more information on this.\n\n    - conda_preserve_egg_dir: Whether to preserve the egg directory as\n      installed by setuptools.  The default is True if the package depends on\n      setuptools or has a setuptools entry_points other than console_scripts\n      and gui_scripts.\n\n    - conda_features: List of features for the package. See the features\n      section of the conda build documentation for more information about\n      features in conda.\n\n    - conda_track_features: List of features that this package should track\n      (enable when installed).  See the features section of the conda build\n      documentation for more information about features in conda.\n\n    Command line options:\n\n    --buildnum: Set the build number. Defaults to the conda_buildnum passed to\n      setup(), or 0. Overrides any conda_buildnum passed to setup().\n\n    \"\"\"\n    # Unfortunately, there's no way to warn the users that they need to use\n    # distclass=CondaDistribution when they try to use a conda option to\n    # setup(). Distribution.__init__ will just print a warning when it sees an\n    # attr it doesn't recognize, and then it is discarded.\n\n    # attr: default\n    conda_attrs = {\n        'conda_buildnum': 0,\n        'conda_buildstr': None,\n        'conda_import_tests': True,\n        'conda_command_tests': True,\n        'conda_binary_relocation': True,\n        'conda_preserve_egg_dir': None,\n        'conda_features': None,\n        'conda_track_features': None,\n        }\n\n    def __init__(self, attrs=None):\n        given_attrs = {}\n        # We need to remove the attrs so that Distribution.__init__ doesn't\n        # warn about them.\n        if attrs:\n            for attr in self.conda_attrs:\n                if attr in attrs:\n                    given_attrs[attr] = attrs.pop(attr)\n\n        if not PY3:\n            # Distribution is an old-style class in Python 3\n            Distribution.__init__(self, attrs)\n        else:\n            super().__init__(attrs)\n\n        for attr in self.conda_attrs:\n            setattr(self.metadata, attr, given_attrs.get(attr, self.conda_attrs[attr]))\n\nclass bdist_conda(install):\n    description = \"create a conda package\"\n\n    def initialize_options(self):\n        if not PY3:\n            # Command is an old-style class in Python 2\n            install.initialize_options(self)\n        else:\n            super().initialize_options()\n        self.buildnum = None\n        self.binstar_upload = False\n\n    def finalize_options(self):\n        opt_dict = self.distribution.get_option_dict('install')\n        if self.prefix:\n            raise DistutilsOptionError(\"--prefix is not allowed\")\n        opt_dict['prefix'] = (\"bdist_conda\", config.build_prefix)\n        if not PY3:\n            # Command is an old-style class in Python 2\n            install.finalize_options(self)\n        else:\n            super().finalize_options()\n\n    def run(self):\n        # Make sure the metadata has the conda attributes, even if the\n        # distclass isn't CondaDistribution. We primarily do this to simplify\n        # the code below.\n\n        metadata = self.distribution.metadata\n\n        for attr in CondaDistribution.conda_attrs:\n            if not hasattr(metadata, attr):\n                setattr(metadata, attr,\n                    CondaDistribution.conda_attrs[attr])\n\n        # The command line takes precedence\n        if self.buildnum is not None:\n            metadata.conda_buildnum = self.buildnum\n\n        with Locked(config.croot):\n            d = defaultdict(dict)\n            # Needs to be lowercase\n            d['package']['name'] = metadata.name\n            d['package']['version'] = metadata.version\n            d['build']['number'] = metadata.conda_buildnum\n\n            # MetaData does the auto stuff if the build string is None\n            d['build']['string'] = metadata.conda_buildstr\n\n            d['build']['binary_relocation'] = metadata.conda_binary_relocation\n            d['build']['preserve_egg_dir'] = metadata.conda_preserve_egg_dir\n            d['build']['features'] = metadata.conda_features\n            d['build']['track_features'] = metadata.conda_track_features\n\n            # XXX: I'm not really sure if it is correct to combine requires\n            # and install_requires\n            d['requirements']['run'] = d['requirements']['build'] = \\\n                [spec_from_line(i) for i in\n                    (metadata.requires or []) +\n                    (getattr(self.distribution, 'install_requires', []) or\n                        [])] + ['python']\n            if hasattr(self.distribution, 'tests_require'):\n                # A lot of packages use extras_require['test'], but\n                # tests_require is the one that is officially supported by\n                # setuptools.\n                d['test']['requires'] = [spec_from_line(i) for i in\n                    self.distribution.tests_require or []]\n\n            d['about']['home'] = metadata.url\n            # Don't worry about classifiers. This isn't skeleton pypi. We\n            # don't need to make this work with random stuff in the wild. If\n            # someone writes their setup.py wrong and this doesn't work, it's\n            # their fault.\n            d['about']['license'] = metadata.license\n            d['about']['summary'] = self.distribution.description\n\n            # This is similar logic from conda skeleton pypi\n            entry_points = getattr(self.distribution, 'entry_points', [])\n            if entry_points:\n                if isinstance(entry_points, string_types):\n                    # makes sure it is left-shifted\n                    newstr = \"\\n\".join(x.strip() for x in\n                        entry_points.split('\\n'))\n                    c = configparser.ConfigParser()\n                    entry_points = {}\n                    try:\n                        c.readfp(StringIO(newstr))\n                    except Exception as err:\n                        # This seems to be the best error here\n                        raise DistutilsGetoptError(\"ERROR: entry-points not understood: \" + str(err) + \"\\nThe string was\" + newstr)\n                    else:\n                        for section in config.sections():\n                            if section in ['console_scripts', 'gui_scripts']:\n                                value = ['%s=%s' % (option, config.get(section, option))\n                                         for option in config.options(section)]\n                                entry_points[section] = value\n                            else:\n                                # Make sure setuptools is added as a dependency below\n                                entry_points[section] = None\n\n                if not isinstance(entry_points, dict):\n                    raise DistutilsGetoptError(\"ERROR: Could not add entry points. They were:\\n\" + entry_points)\n                else:\n                    cs = entry_points.get('console_scripts', [])\n                    gs = entry_points.get('gui_scripts', [])\n                    # We have *other* kinds of entry-points so we need\n                    # setuptools at run-time\n                    if not cs and not gs and len(entry_points) > 1:\n                        d['requirements']['run'].append('setuptools')\n                        d['requirements']['build'].append('setuptools')\n                    entry_list = cs + gs\n                    if gs and conda.config.platform == 'osx':\n                        d['build']['osx_is_app'] = True\n                    if len(cs + gs) != 0:\n                        d['build']['entry_points'] = entry_list\n                        if metadata.conda_command_tests is True:\n                            d['test']['commands'] = list(map(unicode, pypi.make_entry_tests(entry_list)))\n\n            if 'setuptools' in d['requirements']['run']:\n                d['build']['preserve_egg_dir'] = True\n\n\n            if metadata.conda_import_tests:\n                if metadata.conda_import_tests is True:\n                    if self.distribution.packages:\n                        d['test']['imports'] = self.distribution.packages\n                else:\n                    d['test']['imports'] = metadata.conda_import_tests\n\n            if (metadata.conda_command_tests and not\n                isinstance(metadata.conda_command_tests,\n                bool)):\n                d['test']['commands'] = list(map(unicode, metadata.conda_command_tests))\n\n            d = dict(d)\n            m = MetaData.fromdict(d)\n            # Shouldn't fail, but do you really trust the code above?\n            m.check_fields()\n            build.build(m, post=False)\n            # Do the install\n            if not PY3:\n                # Command is an old-style class in Python 2\n                install.run(self)\n            else:\n                super().run()\n            build.build(m, post=True)\n            build.test(m)\n            if self.binstar_upload:\n                class args:\n                    binstar_upload = self.binstar_upload\n                handle_binstar_upload(build.bldpkg_path(m), args)\n            else:\n                no_upload_message = \"\"\"\\\n# If you want to upload this package to binstar.org later, type:\n#\n# $ binstar upload %s\n\"\"\" % build.bldpkg_path(m)\n                print(no_upload_message)\n\n\n# Distutils looks for user_options on the class (not instance).  It also\n# requires that it is an instance of list. So we do this here because we want\n# to keep the options from the superclass (and because I don't feel like\n# making a metaclass just to make this work).\n\n# We have to call str() on the options because our import of\n# __future__.unicode_literals breaks distutils.fancy_getopt in Python 2.\nbdist_conda.user_options.extend([\n    (str('buildnum='), None, str('''The build number of\n    the conda package. Defaults to 0, or the conda_buildnum specified in the\n    setup() function. The command line flag overrides the option to\n    setup().''')),\n    (str('binstar-upload'), None, (\"\"\"Upload the finished package to binstar\"\"\")),\n    ])\n\nbdist_conda.boolean_options.extend([str('binstar-upload')])\n", "path": "bdist_conda.py"}], "after_files": [{"content": "\"\"\"\nbdist_conda\n\n\"\"\"\nfrom __future__ import (print_function, division, unicode_literals,\n    absolute_import)\n\nfrom collections import defaultdict\n\nfrom distutils.command.install import install\nfrom distutils.errors import DistutilsOptionError, DistutilsGetoptError\nfrom distutils.dist import Distribution\n\nfrom conda.compat import (StringIO, string_types, configparser, PY3, text_type\nas unicode)\nfrom conda.lock import Locked\nimport conda.config\nfrom conda.cli.common import spec_from_line\nfrom conda_build.metadata import MetaData\nfrom conda_build import config, build, pypi\nfrom conda_build.main_build import handle_binstar_upload\n\n# TODO: Add support for all the options that conda build has\n\nclass CondaDistribution(Distribution):\n    \"\"\"\n    Distribution subclass that supports bdist_conda options\n\n    This class is required if you want to pass any bdist_conda specific\n    options to setup().  To use, set distclass=CondaDistribution in setup().\n\n    **NOTE**: If you use setuptools, you must import setuptools before\n    importing distutils.commands.bdist_conda.\n\n    Options that can be passed to setup() (must include\n    distclass=CondaDistribution):\n\n    - conda_buildnum: The build number. Defaults to 0. Can be overridden on\n      the command line with the --buildnum flag.\n\n    - conda_buildstr: The build string. Default is generated automatically\n      from the Python version, NumPy version if relevant, and the build\n      number, like py34_0.\n\n    - conda_import_tests: Whether to automatically run import tests. The\n      default is True, which runs import tests for the all the modules in\n      \"packages\". Also allowed are False, which runs no tests, or a list of\n      module names to be tested on import.\n\n    - conda_command_tests: Command line tests to run. Default is True, which\n      runs ``command --help`` for each ``command`` in the console_scripts and\n      gui_scripts entry_points. Also allowed are False, which doesn't run any\n      command tests, or a list of command tests to run.\n\n    - conda_binary_relocation: Whether binary files should be made relocatable\n      (using install_name_tool on OS X or patchelf on Linux). The default is\n      True. See the \"making packages relocatable\" section in the conda build\n      documentation for more information on this.\n\n    - conda_preserve_egg_dir: Whether to preserve the egg directory as\n      installed by setuptools.  The default is True if the package depends on\n      setuptools or has a setuptools entry_points other than console_scripts\n      and gui_scripts.\n\n    - conda_features: List of features for the package. See the features\n      section of the conda build documentation for more information about\n      features in conda.\n\n    - conda_track_features: List of features that this package should track\n      (enable when installed).  See the features section of the conda build\n      documentation for more information about features in conda.\n\n    Command line options:\n\n    --buildnum: Set the build number. Defaults to the conda_buildnum passed to\n      setup(), or 0. Overrides any conda_buildnum passed to setup().\n\n    \"\"\"\n    # Unfortunately, there's no way to warn the users that they need to use\n    # distclass=CondaDistribution when they try to use a conda option to\n    # setup(). Distribution.__init__ will just print a warning when it sees an\n    # attr it doesn't recognize, and then it is discarded.\n\n    # attr: default\n    conda_attrs = {\n        'conda_buildnum': 0,\n        'conda_buildstr': None,\n        'conda_import_tests': True,\n        'conda_command_tests': True,\n        'conda_binary_relocation': True,\n        'conda_preserve_egg_dir': None,\n        'conda_features': None,\n        'conda_track_features': None,\n        }\n\n    def __init__(self, attrs=None):\n        given_attrs = {}\n        # We need to remove the attrs so that Distribution.__init__ doesn't\n        # warn about them.\n        if attrs:\n            for attr in self.conda_attrs:\n                if attr in attrs:\n                    given_attrs[attr] = attrs.pop(attr)\n\n        if not PY3:\n            # Distribution is an old-style class in Python 3\n            Distribution.__init__(self, attrs)\n        else:\n            super().__init__(attrs)\n\n        for attr in self.conda_attrs:\n            setattr(self.metadata, attr, given_attrs.get(attr, self.conda_attrs[attr]))\n\nclass bdist_conda(install):\n    description = \"create a conda package\"\n\n    def initialize_options(self):\n        if not PY3:\n            # Command is an old-style class in Python 2\n            install.initialize_options(self)\n        else:\n            super().initialize_options()\n        self.buildnum = None\n        self.binstar_upload = False\n\n    def finalize_options(self):\n        opt_dict = self.distribution.get_option_dict('install')\n        if self.prefix:\n            raise DistutilsOptionError(\"--prefix is not allowed\")\n        opt_dict['prefix'] = (\"bdist_conda\", config.build_prefix)\n        if not PY3:\n            # Command is an old-style class in Python 2\n            install.finalize_options(self)\n        else:\n            super().finalize_options()\n\n    def run(self):\n        # Make sure the metadata has the conda attributes, even if the\n        # distclass isn't CondaDistribution. We primarily do this to simplify\n        # the code below.\n\n        metadata = self.distribution.metadata\n\n        for attr in CondaDistribution.conda_attrs:\n            if not hasattr(metadata, attr):\n                setattr(metadata, attr,\n                    CondaDistribution.conda_attrs[attr])\n\n        # The command line takes precedence\n        if self.buildnum is not None:\n            metadata.conda_buildnum = self.buildnum\n\n        with Locked(config.croot):\n            d = defaultdict(dict)\n            # Needs to be lowercase\n            d['package']['name'] = metadata.name\n            d['package']['version'] = metadata.version\n            d['build']['number'] = metadata.conda_buildnum\n\n            # MetaData does the auto stuff if the build string is None\n            d['build']['string'] = metadata.conda_buildstr\n\n            d['build']['binary_relocation'] = metadata.conda_binary_relocation\n            d['build']['preserve_egg_dir'] = metadata.conda_preserve_egg_dir\n            d['build']['features'] = metadata.conda_features\n            d['build']['track_features'] = metadata.conda_track_features\n\n            # XXX: I'm not really sure if it is correct to combine requires\n            # and install_requires\n            d['requirements']['run'] = d['requirements']['build'] = \\\n                [spec_from_line(i) for i in\n                    (metadata.requires or []) +\n                    (getattr(self.distribution, 'install_requires', []) or\n                        [])] + ['python']\n            if hasattr(self.distribution, 'tests_require'):\n                # A lot of packages use extras_require['test'], but\n                # tests_require is the one that is officially supported by\n                # setuptools.\n                d['test']['requires'] = [spec_from_line(i) for i in\n                    self.distribution.tests_require or []]\n\n            d['about']['home'] = metadata.url\n            # Don't worry about classifiers. This isn't skeleton pypi. We\n            # don't need to make this work with random stuff in the wild. If\n            # someone writes their setup.py wrong and this doesn't work, it's\n            # their fault.\n            d['about']['license'] = metadata.license\n            d['about']['summary'] = self.distribution.description\n\n            # This is similar logic from conda skeleton pypi\n            entry_points = getattr(self.distribution, 'entry_points', [])\n            if entry_points:\n                if isinstance(entry_points, string_types):\n                    # makes sure it is left-shifted\n                    newstr = \"\\n\".join(x.strip() for x in\n                        entry_points.split('\\n'))\n                    c = configparser.ConfigParser()\n                    entry_points = {}\n                    try:\n                        c.readfp(StringIO(newstr))\n                    except Exception as err:\n                        # This seems to be the best error here\n                        raise DistutilsGetoptError(\"ERROR: entry-points not understood: \" + str(err) + \"\\nThe string was\" + newstr)\n                    else:\n                        for section in config.sections():\n                            if section in ['console_scripts', 'gui_scripts']:\n                                value = ['%s=%s' % (option, config.get(section, option))\n                                         for option in config.options(section)]\n                                entry_points[section] = value\n                            else:\n                                # Make sure setuptools is added as a dependency below\n                                entry_points[section] = None\n\n                if not isinstance(entry_points, dict):\n                    raise DistutilsGetoptError(\"ERROR: Could not add entry points. They were:\\n\" + entry_points)\n                else:\n                    cs = entry_points.get('console_scripts', [])\n                    gs = entry_points.get('gui_scripts', [])\n                    # We have *other* kinds of entry-points so we need\n                    # setuptools at run-time\n                    if not cs and not gs and len(entry_points) > 1:\n                        d['requirements']['run'].append('setuptools')\n                        d['requirements']['build'].append('setuptools')\n                    entry_list = cs + gs\n                    if gs and conda.config.platform == 'osx':\n                        d['build']['osx_is_app'] = True\n                    if len(cs + gs) != 0:\n                        d['build']['entry_points'] = entry_list\n                        if metadata.conda_command_tests is True:\n                            d['test']['commands'] = list(map(unicode, pypi.make_entry_tests(entry_list)))\n\n            if 'setuptools' in d['requirements']['run']:\n                d['build']['preserve_egg_dir'] = True\n\n            if metadata.conda_import_tests:\n                if metadata.conda_import_tests is True:\n                    d['test']['imports'] = ((self.distribution.packages or [])\n                                            + (self.distribution.py_modules or []))\n                else:\n                    d['test']['imports'] = metadata.conda_import_tests\n\n            if (metadata.conda_command_tests and not\n                isinstance(metadata.conda_command_tests,\n                bool)):\n                d['test']['commands'] = list(map(unicode, metadata.conda_command_tests))\n\n            d = dict(d)\n            m = MetaData.fromdict(d)\n            # Shouldn't fail, but do you really trust the code above?\n            m.check_fields()\n            build.build(m, post=False)\n            # Do the install\n            if not PY3:\n                # Command is an old-style class in Python 2\n                install.run(self)\n            else:\n                super().run()\n            build.build(m, post=True)\n            build.test(m)\n            if self.binstar_upload:\n                class args:\n                    binstar_upload = self.binstar_upload\n                handle_binstar_upload(build.bldpkg_path(m), args)\n            else:\n                no_upload_message = \"\"\"\\\n# If you want to upload this package to binstar.org later, type:\n#\n# $ binstar upload %s\n\"\"\" % build.bldpkg_path(m)\n                print(no_upload_message)\n\n\n# Distutils looks for user_options on the class (not instance).  It also\n# requires that it is an instance of list. So we do this here because we want\n# to keep the options from the superclass (and because I don't feel like\n# making a metaclass just to make this work).\n\n# We have to call str() on the options because our import of\n# __future__.unicode_literals breaks distutils.fancy_getopt in Python 2.\nbdist_conda.user_options.extend([\n    (str('buildnum='), None, str('''The build number of\n    the conda package. Defaults to 0, or the conda_buildnum specified in the\n    setup() function. The command line flag overrides the option to\n    setup().''')),\n    (str('binstar-upload'), None, (\"\"\"Upload the finished package to binstar\"\"\")),\n    ])\n\nbdist_conda.boolean_options.extend([str('binstar-upload')])\n", "path": "bdist_conda.py"}]}