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

test_bdist_egg fails with Distribution has no attribute entry_points (Python 3.6+) #836

Closed
jaraco opened this issue Oct 30, 2016 · 16 comments
Labels

Comments

@jaraco
Copy link
Member

jaraco commented Oct 30, 2016

In the last two days, the test_bdist_egg test has started failing on Python 3.6 dev and Python nightly (3.7):

_____________________________ Test.test_bdist_egg ______________________________

self = <setuptools.tests.test_bdist_egg.Test object at 0x7fe3b84166a0>

setup_context = local('/tmp/pytest-of-travis/pytest-0/test_bdist_egg0')

user_override = None

    def test_bdist_egg(self, setup_context, user_override):

        dist = Distribution(dict(

            script_name='setup.py',

            script_args=['bdist_egg'],

            name='foo',

            py_modules=['hi']

        ))

        os.makedirs(os.path.join('build', 'src'))

        with contexts.quiet():

            dist.parse_command_line()

>           dist.run_commands()

/home/travis/build/pypa/setuptools/setuptools/tests/test_bdist_egg.py:40: 

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

/opt/python/3.7-dev/lib/python3.7/distutils/dist.py:955: in run_commands

    self.run_command(cmd)

/opt/python/3.7-dev/lib/python3.7/distutils/dist.py:974: in run_command

    cmd_obj.run()

/home/travis/build/pypa/setuptools/setuptools/command/bdist_egg.py:152: in run

    self.run_command("egg_info")

/opt/python/3.7-dev/lib/python3.7/distutils/cmd.py:313: in run_command

    self.distribution.run_command(command)

/opt/python/3.7-dev/lib/python3.7/distutils/dist.py:974: in run_command

    cmd_obj.run()

/home/travis/build/pypa/setuptools/setuptools/command/egg_info.py:272: in run

    writer(self, ep.name, os.path.join(self.egg_info, ep.name))

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cmd = <setuptools.command.egg_info.egg_info object at 0x7fe3b82f0a90>

basename = 'entry_points.txt', filename = 'foo.egg-info/entry_points.txt'

    def write_entries(cmd, basename, filename):

>       ep = cmd.distribution.entry_points

E       AttributeError: 'Distribution' object has no attribute 'entry_points'

/home/travis/build/pypa/setuptools/setuptools/command/egg_info.py:669: AttributeError

=== 1 failed, 181 passed, 11 skipped, 2 xfailed, 2 xpassed in 46.12 seconds ====

ERROR: InvocationError: '/home/travis/build/pypa/setuptools/.tox/python/bin/python -m pytest -rsx'
@jaraco
Copy link
Member Author

jaraco commented Oct 30, 2016

Checking the CPython logs, I don't see anything there that I would have expected to trigger this behavior.

@jaraco
Copy link
Member Author

jaraco commented Oct 30, 2016

I've decided to go back to the last successful build, and I selected this job which was passing and restarted it. If it passes, then we know the issue is with the setuptools code, but if it fails, then we know something changed with the CPython code in Travis.

@jaraco
Copy link
Member Author

jaraco commented Oct 30, 2016

And it fails, so the issue must be related to CPython code having changed on Travis.

@jaraco
Copy link
Member Author

jaraco commented Oct 30, 2016

The Travis output doesn't give enough information about which version of Python was being used.

$ python --version
Python 3.6.0b2+

@jaraco
Copy link
Member Author

jaraco commented Oct 30, 2016

I'm unable to troubleshoot because of the issue with tox rooted in a long-standing issue with virtualenv.

@jaraco
Copy link
Member Author

jaraco commented Oct 31, 2016

Bypassing tox and using rwt, I was able to replicate the failure:

$ ~/p/cpython/python.exe -m rwt -r tests/requirements.txt -- -m pytest
Collecting pytest-flake8 (from -r tests/requirements.txt (line 1))
  Using cached pytest_flake8-0.8.1-py2.py3-none-any.whl
Collecting pytest>=3.0.2 (from -r tests/requirements.txt (line 2))
  Using cached pytest-3.0.3-py2.py3-none-any.whl
Collecting setuptools[ssl] (from -r tests/requirements.txt (line 3))
  Using cached setuptools-28.7.1-py2.py3-none-any.whl
Collecting backports.unittest_mock>=1.2 (from -r tests/requirements.txt (line 4))
  Using cached backports.unittest_mock-1.2-py2.py3-none-any.whl
Collecting flake8>=3.0 (from pytest-flake8->-r tests/requirements.txt (line 1))
  Using cached flake8-3.0.4-py2.py3-none-any.whl
Collecting py>=1.4.29 (from pytest>=3.0.2->-r tests/requirements.txt (line 2))
  Using cached py-1.4.31-py2.py3-none-any.whl
Collecting mccabe<0.6.0,>=0.5.0 (from flake8>=3.0->pytest-flake8->-r tests/requirements.txt (line 1))
  Using cached mccabe-0.5.2-py2.py3-none-any.whl
Collecting pycodestyle<2.1.0,>=2.0.0 (from flake8>=3.0->pytest-flake8->-r tests/requirements.txt (line 1))
  Using cached pycodestyle-2.0.0-py2.py3-none-any.whl
Collecting pyflakes!=1.2.0,!=1.2.1,!=1.2.2,<1.3.0,>=0.8.1 (from flake8>=3.0->pytest-flake8->-r tests/requirements.txt (line 1))
  Using cached pyflakes-1.2.3-py2.py3-none-any.whl
Installing collected packages: py, pytest, mccabe, pycodestyle, pyflakes, flake8, pytest-flake8, setuptools, backports.unittest-mock
Successfully installed backports.unittest-mock flake8 mccabe py pycodestyle pyflakes pytest pytest-flake8 setuptools-27.1.2
======================================= test session starts ========================================
platform darwin -- Python 3.6.0b2+, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
rootdir: /Users/jaraco/Dropbox/code/main/setuptools, inifile: pytest.ini
plugins: flake8-0.8.1, backports.unittest-mock-1.2
collected 196 items / 2 skipped 

pkg_resources/api_tests.txt .
pkg_resources/tests/test_markers.py .
pkg_resources/tests/test_pkg_resources.py ....
pkg_resources/tests/test_resources.py .................................................
setuptools/namespaces.py .
setuptools/package_index.py .
setuptools/sandbox.py .
setuptools/command/develop.py .
setuptools/command/install_lib.py .
setuptools/command/py36compat.py .
setuptools/tests/test_archive_util.py X
setuptools/tests/test_bdist_egg.py F
setuptools/tests/test_build_ext.py ..
setuptools/tests/test_build_py.py .
setuptools/tests/test_develop.py .s
setuptools/tests/test_dist_info.py ..
setuptools/tests/test_easy_install.py ......X...................
setuptools/tests/test_egg_info.py ..........
setuptools/tests/test_find_packages.py ............
setuptools/tests/test_install_scripts.py .s.s
setuptools/tests/test_integration.py .x.x
setuptools/tests/test_manifest.py ......................
setuptools/tests/test_packageindex.py .................
setuptools/tests/test_sandbox.py .s.........
setuptools/tests/test_sdist.py ..........
setuptools/tests/test_setuptools.py ...
setuptools/tests/test_test.py .
setuptools/tests/test_unicode_utils.py .
setuptools/tests/test_upload_docs.py ..
setuptools/tests/test_windows_wrappers.py sss

============================================= FAILURES =============================================
_______________________________________ Test.test_bdist_egg ________________________________________

self = <setuptools.tests.test_bdist_egg.Test object at 0x110977b38>
setup_context = local('/private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pytest-of-jaraco/pytest-7/test_bdist_egg0')
user_override = None

    def test_bdist_egg(self, setup_context, user_override):
        dist = Distribution(dict(
            script_name='setup.py',
            script_args=['bdist_egg'],
            name='foo',
            py_modules=['hi']
        ))
        os.makedirs(os.path.join('build', 'src'))
        with contexts.quiet():
            dist.parse_command_line()
>           dist.run_commands()

/Users/jaraco/Dropbox/code/main/setuptools/setuptools/tests/test_bdist_egg.py:40: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/Users/jaraco/p/cpython/Lib/distutils/dist.py:955: in run_commands
    self.run_command(cmd)
/Users/jaraco/p/cpython/Lib/distutils/dist.py:974: in run_command
    cmd_obj.run()
/Users/jaraco/Dropbox/code/main/setuptools/setuptools/command/bdist_egg.py:152: in run
    self.run_command("egg_info")
/Users/jaraco/p/cpython/Lib/distutils/cmd.py:313: in run_command
    self.distribution.run_command(command)
/Users/jaraco/p/cpython/Lib/distutils/dist.py:974: in run_command
    cmd_obj.run()
/Users/jaraco/Dropbox/code/main/setuptools/setuptools/command/egg_info.py:272: in run
    writer(self, ep.name, os.path.join(self.egg_info, ep.name))
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cmd = <setuptools.command.egg_info.egg_info object at 0x110b9cd30>, basename = 'entry_points.txt'
filename = 'foo.egg-info/entry_points.txt'

    def write_entries(cmd, basename, filename):
>       ep = cmd.distribution.entry_points
E       AttributeError: 'Distribution' object has no attribute 'entry_points'

/Users/jaraco/Dropbox/code/main/setuptools/setuptools/command/egg_info.py:669: AttributeError
============== 1 failed, 184 passed, 9 skipped, 2 xfailed, 2 xpassed in 50.53 seconds ==============

Interestingly, the failure doesn't happen if the tests are downselected:

$ ~/p/cpython/python.exe -m rwt -q -r tests/requirements.txt -- -m pytest -k test_bdist_egg
======================================= test session starts ========================================
platform darwin -- Python 3.6.0b2+, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
rootdir: /Users/jaraco/Dropbox/code/main/setuptools, inifile: pytest.ini
plugins: flake8-0.8.1, backports.unittest-mock-1.2
collected 196 items / 2 skipped 

setuptools/tests/test_bdist_egg.py .
setuptools/tests/test_easy_install.py .

======================================= 194 tests deselected =======================================
======================= 2 passed, 2 skipped, 194 deselected in 0.81 seconds ========================

Therefore, the failure is almost certainly an interaction with other tests.

@jaraco
Copy link
Member Author

jaraco commented Oct 31, 2016

Passing --ignore pkg_resources --ignore setuptools/command also seems to bypass the failure but just ignoring one or the other does not, so the interaction goes deeper.

@jaraco
Copy link
Member Author

jaraco commented Oct 31, 2016

I can bypass the failures with just these ignores: $ ~/p/cpython/python.exe -m rwt -q -r tests/requirements.txt -- -m pytest -x --ignore setuptools/command/py36compat.py --ignore pkg_resources/tests/test_pkg_resources.py

@jaraco
Copy link
Member Author

jaraco commented Oct 31, 2016

Okay, this is interesting - disabling doctest as so,

diff -r 6fa02ce50651 pytest.ini
--- a/pytest.ini    Mon Oct 31 09:15:43 2016 -0400
+++ b/pytest.ini    Mon Oct 31 11:41:28 2016 -0400
@@ -1,5 +1,5 @@
 [pytest]
-addopts=--doctest-modules --ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/shlib_test --doctest-glob=pkg_resources/api_tests.txt --ignore scripts/upload-old-releases-as-zip.py --ignore pavement.py
+addopts=--ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/shlib_test --ignore scripts/upload-old-releases-as-zip.py --ignore pavement.py
 norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern .*
 flake8-ignore =
     setuptools/site-patch.py F821

I'm able to work around pytest-dev/pytest#985 and get a pdb prompt at the failure (even though py36compat is no longer being tested.

@jaraco
Copy link
Member Author

jaraco commented Oct 31, 2016

It appears as if Distribution.parse_command_line is implicated.

$ hg diff                                                                         
diff -r 6fa02ce50651 pytest.ini
--- a/pytest.ini    Mon Oct 31 09:15:43 2016 -0400
+++ b/pytest.ini    Mon Oct 31 11:59:49 2016 -0400
@@ -1,5 +1,5 @@
 [pytest]
-addopts=--doctest-modules --ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/shlib_test --doctest-glob=pkg_resources/api_tests.txt --ignore scripts/upload-old-releases-as-zip.py --ignore pavement.py
+addopts=--ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/shlib_test --ignore scripts/upload-old-releases-as-zip.py --ignore pavement.py
 norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern .*
 flake8-ignore =
     setuptools/site-patch.py F821
diff -r 6fa02ce50651 setuptools/tests/test_bdist_egg.py
--- a/setuptools/tests/test_bdist_egg.py    Mon Oct 31 09:15:43 2016 -0400
+++ b/setuptools/tests/test_bdist_egg.py    Mon Oct 31 11:59:49 2016 -0400
@@ -36,7 +36,9 @@
         ))
         os.makedirs(os.path.join('build', 'src'))
         with contexts.quiet():
+            dist.entry_points
             dist.parse_command_line()
+            dist.entry_points
             dist.run_commands()

         # let's see if we got our egg link at the right place
$ ~/p/cpython/python.exe -m rwt -q -r tests/requirements.txt -- -m pytest -x --pdb
======================================= test session starts ========================================
platform darwin -- Python 3.6.0b2+, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
rootdir: /Users/jaraco/Dropbox/code/main/setuptools, inifile: pytest.ini
plugins: flake8-0.8.1, backports.unittest-mock-1.2
collected 189 items / 1 skipped 

pkg_resources/tests/test_markers.py .
pkg_resources/tests/test_pkg_resources.py ....
pkg_resources/tests/test_resources.py .................................................
setuptools/tests/test_archive_util.py X
setuptools/tests/test_bdist_egg.py F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

self = <setuptools.tests.test_bdist_egg.Test object at 0x10af67048>
setup_context = local('/private/var/folders/c6/v7hnmq453xb6p2dbz1gqc6rr0000gn/T/pytest-of-jaraco/pytest-37/test_bdist_egg0')
user_override = None

    def test_bdist_egg(self, setup_context, user_override):
        dist = Distribution(dict(
            script_name='setup.py',
            script_args=['bdist_egg'],
            name='foo',
            py_modules=['hi']
        ))
        os.makedirs(os.path.join('build', 'src'))
        with contexts.quiet():
            dist.entry_points
            dist.parse_command_line()
>           dist.entry_points
E           AttributeError: 'Distribution' object has no attribute 'entry_points'

/Users/jaraco/Dropbox/code/main/setuptools/setuptools/tests/test_bdist_egg.py:41: AttributeError

@jaraco
Copy link
Member Author

jaraco commented Oct 31, 2016

This gets weirder the deeper I dig.

setuptools/tests/test_bdist_egg.py 
>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PDB set_trace (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /Users/jaraco/Dropbox/code/main/setuptools/setuptools/dist.py(348)parse_command_line()
-> result = _Distribution.parse_command_line(self)
(Pdb) self
<setuptools.dist.Distribution object at 0x10ed49dd8>
(Pdb) s
--Call--
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(428)parse_command_line()
-> def parse_command_line(self):
(Pdb) n
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(451)parse_command_line()
-> toplevel_options = self._get_toplevel_options()
(Pdb) n
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(460)parse_command_line()
-> self.commands = []
(Pdb) self.entry_points
(Pdb) n
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(461)parse_command_line()
-> parser = FancyGetopt(toplevel_options + self.display_options)
(Pdb) n
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(462)parse_command_line()
-> parser.set_negative_aliases(self.negative_opt)
(Pdb) n
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(463)parse_command_line()
-> parser.set_aliases({'licence': 'license'})
(Pdb) n
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(464)parse_command_line()
-> args = parser.getopt(args=self.script_args, object=self)
(Pdb) self.entry_points
*** AttributeError: 'Distribution' object has no attribute 'entry_points'

It looks like self.entry_points is getting cleared by accessing self.display_options or self.negative_opt. I don't see anything else there that could be affecting vars(self).

@jaraco
Copy link
Member Author

jaraco commented Oct 31, 2016

Actually, it seems like it's the setting of self.commands that's clearing self.entry_points.

> /Users/jaraco/p/cpython/Lib/distutils/dist.py(461)parse_command_line()
-> parser = FancyGetopt(toplevel_options + self.display_options)
(Pdb) self.entry_points
*** AttributeError: 'Distribution' object has no attribute 'entry_points'

@jaraco
Copy link
Member Author

jaraco commented Oct 31, 2016

I'm starting to think there's an inherent flaw in the data model of Python:

setuptools/tests/test_bdist_egg.py 
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PDB set_trace (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /Users/jaraco/Dropbox/code/main/setuptools/setuptools/dist.py(348)parse_command_line()
-> result = _Distribution.parse_command_line(self)
(Pdb) s
--Call--
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(428)parse_command_line()
-> def parse_command_line(self):
(Pdb) n
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(451)parse_command_line()
-> toplevel_options = self._get_toplevel_options()
(Pdb) n
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(460)parse_command_line()
-> self.commands = []
(Pdb) 'entry_points' in vars(self)
True
(Pdb) 'commands' in vars(self)
False
(Pdb) self.commands
*** AttributeError: 'Distribution' object has no attribute 'commands'
(Pdb) self.commands = []
(Pdb) 'entry_points' in vars(self)
False

@jaraco
Copy link
Member Author

jaraco commented Oct 31, 2016

Yes, there's definitely something wrong with our dear dictionary:

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> PDB set_trace (IO-capturing turned off) >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /Users/jaraco/Dropbox/code/main/setuptools/setuptools/dist.py(348)parse_command_line()
-> result = _Distribution.parse_command_line(self)
(Pdb) s
--Call--
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(428)parse_command_line()
-> def parse_command_line(self):
(Pdb) n
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(451)parse_command_line()
-> toplevel_options = self._get_toplevel_options()
(Pdb) n
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(460)parse_command_line()
-> self.commands = []
(Pdb) len(vars(self))
89
(Pdb) n
> /Users/jaraco/p/cpython/Lib/distutils/dist.py(461)parse_command_line()
-> parser = FancyGetopt(toplevel_options + self.display_options)
(Pdb) len(vars(self))
90
(Pdb) len(vars(self))
90
(Pdb) for var in vars(self): pass
(Pdb) len(vars(self))
90
(Pdb) len(set(vars(self)))
71
(Pdb) len(vars(self))
90
(Pdb) self.__dict__.keys()
dict_keys(['package_data', 'require_features', 'features', 'dist_files', 'src_root', 'dependency_links', 'verbose', 'dry_run', 'help', 'help_commands', 'name', 'version', 'fullname', 'author', 'author_email', 'maintainer', 'maintainer_email', 'contact', 'contact_email', 'url', 'license', 'licence', 'description', 'long_description', 'platforms', 'classifiers', 'keywords', 'provides', 'requires', 'obsoletes', 'metadata', 'get_name', 'get_version', 'get_author', 'get_author_email', 'get_maintainer', 'get_maintainer_email', 'get_url', 'get_license', 'get_description', 'get_long_description', 'get_keywords', 'get_platforms', 'get_fullname', 'get_contact', 'get_contact_email', 'get_classifiers', 'get_download_url', 'get_provides', 'get_requires', 'get_obsoletes', 'cmdclass', 'command_packages', 'script_name', 'script_args', 'command_options', 'package_dir', 'py_modules', 'libraries', 'headers', 'ext_modules', 'ext_package', 'include_dirs', 'extra_path', 'scripts', 'data_files', 'password', 'command_obj', 'have_run', 'want_user_cfg', 'commands'])
(Pdb) len(self.__dict__)
90

@jaraco
Copy link
Member Author

jaraco commented Oct 31, 2016

Given that the issue appears to be a Python bug, I'm going to bisect the changes between here and there to identify the offending commit, looking primarily at commits between when the tests last passed and when the tests started failing.

@jaraco
Copy link
Member Author

jaraco commented Oct 31, 2016

The issue begins with 6b88dfc7b25d implicating python 28199.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant