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

Fixed #30948 -- Changed packaging to use setuptools declarative config in setup.cfg. #12013

Merged
merged 2 commits into from Nov 8, 2019
Merged

Fixed #30948 -- Changed packaging to use setuptools declarative config in setup.cfg. #12013

merged 2 commits into from Nov 8, 2019

Conversation

jdufresne
Copy link
Member

setuptools allows using setup.cfg to define a package’s metadata and other options that are normally supplied to the setup(). This declarative approach avoids the need for some logic in setup.py which can help reduce boilerplate and support automation with external tools.

Available since setuptools 30.3.0 (8 Dec 2016).

https://code.djangoproject.com/ticket/30948

@jdufresne
Copy link
Member Author

I was considering also removing the Python version check at the top of setup.py as I feel it duplicates the python_requires option which has been supported since pip 9.0.0 (2016-11-02). Any thoughts on this?

Same thought for the the overlay warning. Is this warning & logic still relevant with modern pip and setuptools?

@jdufresne
Copy link
Member Author

Same thought for the the overlay warning. Is this warning & logic still relevant with modern pip and setuptools?

We should always be recommending pip to install Django andpip handles this automatically. For example, installing Django 2.2 over Django 2.1 shows:

Installing collected packages: sqlparse, Django
  Found existing installation: Django 2.1.13
    Uninstalling Django-2.1.13:
      Successfully uninstalled Django-2.1.13
Successfully installed Django-2.2.6 sqlparse-0.3.0

If we decide to remove the overlay warning, I could also ensure all docs recommend pip for installing.

Copy link
Member

@ngnpope ngnpope left a comment

Choose a reason for hiding this comment

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

Thanks for taking this on Jon. I've been meaning to get around to it.

Could you also add a pyproject.toml with the following:

[build-system]
requires = ['setuptools>=30.3.0', 'wheel>=0.32.0']

You may want to check whether we need newer versions than those I've specified - I just went with the minimum for setuptools for support of metadata in setup.cfg.

+1 to getting rid of the custom version check as python_requires has been around long enough now and this was originally introduced to deal with Python 2 support being dropped in Django 1.11 and python_requires being a bit too new. When 3.1 is released, Python 2 will have been EOL for 8 months. I also think this is sort of misleading now as the required Python version has been been bumped, but the instructions lead toward installing Django < 2.0, which isn't necessarily correct.

+1 to getting rid of the overlay warning. Using python setup.py install has been deprecated for a long time and use of pip install . or pip install --editable . is recommended.

P.S. There is also a spelling mistake in the commit message: declaritivedeclarative.

setup.cfg Outdated
author_email = foundation@djangoproject.com
description = A high-level Python Web framework that encourages rapid development and clean, pragmatic design.
long_description = file: README.rst
license = BSD
Copy link
Member

Choose a reason for hiding this comment

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

I know that this was taken over from what is currently there in setup.py, I think we should be more clear.

Strictly speaking, Python is licensed under BSD 3-Clause "New" or "Revised" License which isn't clear from this and is not possible to specify in the classifiers. We should either use the full name or, better yet, the SPDX identifier: BSD-3-Clause. [1]

In addition, Django contains a copy of Python License 2.0 which was added as in the past some code was copied from the Python Standard Library. Strictly speaking that means that Django is (or should be) distributed under the terms of both licenses. That is also not clear here. Looking back through the history of LICENSE.python it was originally added for a backport of weakref which has since been removed. I thought that we could potentially remove it from master again, but grepping for the license filename, we have custom version of urllib.parse.parse_qsl() as django.utils.http.limited_parse_qsl(). [2]

So it seems to me that perhaps we ought to make this license = (BSD-3-Clause AND Python-2.0) and add License :: OSI Approved :: Python Software Foundation License to classifiers.

@carltongibson @felixxm I think this needs some looking into what the correct course of action is.

[1] Note that there is an ongoing discussion on how to make this better in the (probably now, not too far) future, but I think we can do better in the short term.
[2] I see that Python 3.8 has a max_num_fields argument that mirrors the fields_limit argument in the custom version - am prepping a PR to handle updating to a backport from 3.8 instead of the current implementation so that it can eventually be removed.

Copy link
Member

Choose a reason for hiding this comment

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

PR to handle updating to a backport from 3.8

See #12017.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'll happily adjust the license lines to the most correct and explicit. I'll await instruction from @carltongibson @felixxm on how to proceed.

setup.cfg Outdated Show resolved Hide resolved
setup.cfg Show resolved Hide resolved
setup.cfg Outdated Show resolved Hide resolved
@jdufresne jdufresne changed the title Fixed #30948 -- Changed packaging to use setuptools declaritive config in setup.cfg. Fixed #30948 -- Changed packaging to use setuptools declarative config in setup.cfg. Nov 4, 2019
@jdufresne
Copy link
Member Author

Could you also add a pyproject.toml with the following:

Can you link or provide information as to what this will solve? In other words, when will this influence an action by a packager? I've been testing using python3 setup.py sdist bdist_wheel and other commands.


All remaining feedback from the review has been incorporated. Thanks!

@ngnpope
Copy link
Member

ngnpope commented Nov 4, 2019

+1 to getting rid of the overlay warning. Using python setup.py install has been deprecated for a long time and use of pip install . or pip install --editable . is recommended.

I think that we also need to update the following:

diff --git a/INSTALL b/INSTALL
index eb9cf6eaf2..c24c4bb028 100644
--- a/INSTALL
+++ b/INSTALL
@@ -1,10 +1,9 @@
 Thanks for downloading Django.
 
 To install it, make sure you have Python 3.6 or greater installed. Then run
-this command from the command prompt:
+the following commands from the command prompt:
 
-    python setup.py install
-
-If you're upgrading from a previous version, you need to remove it first.
+    python -m ensurepip
+    python -m pip install .
 
 For more detailed instructions, see docs/intro/install.txt.
diff --git a/docs/faq/troubleshooting.txt b/docs/faq/troubleshooting.txt
index ba44aa83ef..f90d0e8e6e 100644
--- a/docs/faq/troubleshooting.txt
+++ b/docs/faq/troubleshooting.txt
@@ -14,9 +14,9 @@ Problems running ``django-admin``
 -----------------------------------
 
 :doc:`django-admin </ref/django-admin>` should be on your system path if you
-installed Django via ``python setup.py``. If it's not on your path, you can
-find it in ``site-packages/django/bin``, where ``site-packages`` is a directory
-within your Python installation. Consider symlinking to :doc:`django-admin
+installed Django via ``pip``. If it's not on your path, you can find it in
+``site-packages/django/bin``, where ``site-packages`` is a directory within
+your Python installation. Consider symlinking to :doc:`django-admin
 </ref/django-admin>` from some place on your path, such as
 :file:`/usr/local/bin`.
 
diff --git a/docs/topics/auth/passwords.txt b/docs/topics/auth/passwords.txt
index 44e80911ba..134ef14583 100644
--- a/docs/topics/auth/passwords.txt
+++ b/docs/topics/auth/passwords.txt
@@ -89,7 +89,7 @@ To use Argon2 as your default storage algorithm, do the following:
 #. Install the `argon2-cffi library`_.  This can be done by running
    ``python -m pip install django[argon2]``, which is equivalent to
    ``python -m pip install argon2-cffi`` (along with any version requirement
-   from Django's ``setup.py``).
+   from Django's ``setup.cfg``).
 
 #. Modify :setting:`PASSWORD_HASHERS` to list ``Argon2PasswordHasher`` first.
    That is, in your settings file, you'd put::
@@ -119,7 +119,7 @@ To use Bcrypt as your default storage algorithm, do the following:
 #. Install the `bcrypt library`_. This can be done by running
    ``python -m pip install django[bcrypt]``, which is equivalent to
    ``python -m pip install bcrypt`` (along with any version requirement from
-   Django's ``setup.py``).
+   Django's ``setup.cfg``).
 
 #. Modify :setting:`PASSWORD_HASHERS` to list ``BCryptSHA256PasswordHasher``
    first. That is, in your settings file, you'd put::

We should probably also update scripts/rpm-install.sh, but I don't know much about the RPM packaging.

@ngnpope
Copy link
Member

ngnpope commented Nov 4, 2019

Can you link or provide information as to what this will solve? In other words, when will this influence an action by a packager? I've been testing using python3 setup.py sdist bdist_wheel and other commands.

pip will use the pyproject.toml to ensure that it has the required dependencies to build the package. In this case it'll ensure that it has a new enough setuptools and wheel. I chose the version of setuptools based on what was required for configuration in setup.cfg; the version of wheel was admittedly subjective. (It probably isn't essential, as pyproject.toml came after the changes in setuptools that we are interested in...)

You can find out more at the following links:

@jdufresne
Copy link
Member Author

jdufresne commented Nov 4, 2019

Trying to specify old setuptools results in:

Some build dependencies for file:///home/jon/devel/django conflict with PEP 517/518 supported requirements: setuptools==30.2.1 is incompatible with setuptools>=40.8.0.

So I'll use this as the minimum setuptools unless someone has a reason for a different version.

@jdufresne
Copy link
Member Author

Thanks for all the helpful feedback! Changes:

  • Applied patch to fix docs
  • Added pyproject.toml
  • Added Co-authored-by to acknowledge the helpful contributions! 🙂

Copy link
Member

@carltongibson carltongibson left a comment

Choose a reason for hiding this comment

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

Hi @jdufresne @pope1ni.

Thanks for this. A few things going on:

  1. The headline move to setup.cfg
  2. Adding pyproject.toml as well.
  3. Removing the Python 2.7 warning.
  4. Removing the overlay warning.
  5. Clarifying the licence. Comment

Maybe 1 & 2 can be addressed together, but I'd prefer to break things up into smaller changes, to be assessed individually.

On 5, first of all gulp. :) — I need to look into this. I'm going to ask @ubernostrum if it's something the board might be able to advise on?

On 3, would you think me particularly old-fashioned if I said that I'd prefer to hold-off on dropping this until well after the Python 2.7 EOL, e.g. until the end of 2020 say? With modern versions of... — yes, but I take it we're catching the case of someone who just happens to have some Python, they know not what installed and downloads Django to find an inconsistency. It costs us nothing to have this here for a season longer.

On 4, not sure what to say immediately, but (as per above) I'd like to address it separately if that's OK.

On 1 and 2, I'm inclined towards being super conservative here. (For all manner of reasons, the main one being I don't want to break anything.) But it looks OK at first glance, so trimmed down, and with a bit of time to play with, no doubt you'll persuade us.

Super effort. Thank you.

@ngnpope
Copy link
Member

ngnpope commented Nov 5, 2019

Maybe 1 & 2 can be addressed together, but I'd prefer to break things up into smaller changes, to be assessed individually.

Sure.

On 5, first of all gulp. :) — I need to look into this. I'm going to ask @ubernostrum if it's something the board might be able to advise on?

Heh. Yeah. I figured that this was an oversight w.r.t. to the dual licensing bit. But would be good to clarify. It just doesn't quite feel right...

On 3, would you think me particularly old-fashioned if I said that I'd prefer to hold-off on dropping this until well after the Python 2.7 EOL, e.g. until the end of 2020 say? With modern versions of... — yes, but I take it we're catching the case of someone who just happens to have some Python, they know not what installed and downloads Django to find an inconsistency. It costs us nothing to have this here for a season longer.

I figure that there isn't much difference between August 2020 - when Django 3.1 will be release with these changes - and December 2020... If we decide to keep it until Django 3.2, let's put a comment in to that effect so that we can remember to scrub it when development on 3.2 starts.

On 4, not sure what to say immediately, but (as per above) I'd like to address it separately if that's OK.

Yeah. So I think the main issue here is due to installing on top of an existing install in the global site packages using python setup.py install which is discouraged. Hence the update to INSTALL to use pip instead. The main outstanding thing here is the bit related to RPM installs.

On 1 and 2, I'm inclined towards being super conservative here. (For all manner of reasons, the main one being I don't want to break anything.) But it looks OK at first glance, so trimmed down, and with a bit of time to play with, no doubt you'll persuade us.

I don't believe we've changed anything of substance? Do correct me if I'm wrong... 😛

Super effort. Thank you.

A pleasure!

@carltongibson
Copy link
Member

I don't believe we've changed anything of substance?

It doesn't look that way, but let's trim it down and then we'll see.

@jdufresne
Copy link
Member Author

Clarifying the licence ...

Why don't we punt on this action item then? This PR keeps the license exactly as it was before, no change. If there is a decision made that the license needs updating, that can easily happen after this lands (or even before, really). Whether or not we're using setup.cfg makes no difference on precising the license. Do we all agree?

@carltongibson
Copy link
Member

carltongibson commented Nov 5, 2019

Yep, that's fine. (It's something to handle apart from this PR.) (Super happy to look at it but...)

@jdufresne
Copy link
Member Author

On 3, would you think me particularly old-fashioned if I said that I'd prefer to hold-off on dropping this until well after the Python 2.7 EOL, e.g. until the end of 2020 say? With modern versions of... — yes, but I take it we're catching the case of someone who just happens to have some Python, they know not what installed and downloads Django to find an inconsistency. It costs us nothing to have this here for a season longer.

Yup, I can add that back, but some context:

Here is my experience installing Django with pip on Python 2. Notice it chooses Django 1.11, not Django 2.0+.

$ pip install django
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Collecting django
  Downloading https://files.pythonhosted.org/packages/cf/19/632a613bc37bbf890f9323ba09374ce9af1d70bb4cba7ff4d3e5e0991b47/Django-1.11.26-py2.py3-none-any.whl (6.9MB)
     |████████████████████████████████| 7.0MB 3.9MB/s
Collecting pytz
  Using cached https://files.pythonhosted.org/packages/e7/f9/f0b53f88060247251bf481fa6ea62cd0d25bf1b11a87888e53ce5b7c8ad2/pytz-2019.3-py2.py3-none-any.whl
Installing collected packages: pytz, django
Successfully installed django-1.11.26 pytz-2019.3

Without this version checking, here is my experience installing Django 3.1 from the source directory using Python 2

$ pip install .
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Processing /home/jon/devel/django
  Installing build dependencies ... done
  Getting requirements to build wheel ... error
  ERROR: Command errored out with exit status 1:
   command: /home/jon/test/v/bin/python2 /home/jon/test/v/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py get_requires_for_build_wheel /tmp/tmpNt7Y0Y
       cwd: /tmp/pip-req-build-85tspF
  Complete output (42 lines):
  Traceback (most recent call last):
    File "/home/jon/test/v/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py", line 257, in <module>
      main()
    File "/home/jon/test/v/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py", line 240, in main
      json_out['return_val'] = hook(**hook_input['kwargs'])
    File "/home/jon/test/v/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py", line 91, in get_requires_for_build_wheel
      return hook(config_settings)
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/build_meta.py", line 146, in get_requires_for_build_wheel
      return self._get_build_requires(config_settings, requirements=['wheel'])
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/build_meta.py", line 127, in _get_build_requires
      self.run_setup()
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/build_meta.py", line 237, in run_setup
      self).run_setup(setup_script=setup_script)
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/build_meta.py", line 142, in run_setup
      exec(compile(code, __file__, 'exec'), locals())
    File "setup.py", line 3, in <module>
      setup()
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/__init__.py", line 145, in setup
      return distutils.core.setup(**attrs)
    File "/usr/lib64/python2.7/distutils/core.py", line 124, in setup
      dist.parse_config_files()
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/dist.py", line 701, in parse_config_files
      ignore_option_errors=ignore_option_errors)
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/config.py", line 121, in parse_configuration
      meta.parse()
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/config.py", line 426, in parse
      section_parser_method(section_options)
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/config.py", line 399, in parse_section
      self[name] = value
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/config.py", line 184, in __setitem__
      value = parser(value)
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/config.py", line 514, in _parse_version
      version = self._parse_attr(value, self.package_dir)
    File "/tmp/pip-build-env-mglrZa/overlay/lib/python2.7/site-packages/setuptools/config.py", line 349, in _parse_attr
      module = import_module(module_name)
    File "/usr/lib64/python2.7/importlib/__init__.py", line 37, in import_module
      __import__(name)
    File "/tmp/pip-req-build-85tspF/django/__init__.py", line 1, in <module>
      from django.utils.version import get_version
    File "/tmp/pip-req-build-85tspF/django/utils/version.py", line 71, in <module>
      @functools.lru_cache()
  AttributeError: 'module' object has no attribute 'lru_cache'
  ----------------------------------------
ERROR: Command errored out with exit status 1: /home/jon/test/v/bin/python2 /home/jon/test/v/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py get_requires_for_build_wheel /tmp/tmpNt7Y0Y Check the logs for full command output.

With the error messaging reinstated:

$ pip install .
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Processing /home/jon/devel/django
  Installing build dependencies ... done
  Getting requirements to build wheel ... error
  ERROR: Command errored out with exit status 1:
   command: /home/jon/test/v/bin/python2 /home/jon/test/v/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py get_requires_for_build_wheel /tmp/tmp58SeFP
       cwd: /tmp/pip-req-build-rfm95q
  Complete output (20 lines):
  
  ==========================
  Unsupported Python version
  ==========================
  
  This version of Django requires Python 3.6, but you're trying to
  install it on Python 2.7.
  
  This may be because you are using a version of pip that doesn't
  understand the python_requires classifier. Make sure you
  have pip >= 9.0 and setuptools >= 24.2, then try again:
  
      $ python -m pip install --upgrade pip setuptools
      $ python -m pip install django
  
  This will install the latest version of Django which works on your
  version of Python. If you can't upgrade your pip (or Python), request
  an older version of Django:
  
      $ python -m pip install "django<2"
  ----------------------------------------
ERROR: Command errored out with exit status 1: /home/jon/test/v/bin/python2 /home/jon/test/v/lib/python2.7/site-packages/pip/_vendor/pep517/_in_process.py get_requires_for_build_wheel /tmp/tmp58SeFP Check the logs for full command output.

Installing without the check certainly has less pleasant error message, but it will not allow the user to proceed with an incompatible version. From your perspective, is this all about the user messaging?

I figure that there isn't much difference between August 2020 - when Django 3.1 will be release with these changes - and December 2020... If we decide to keep it until Django 3.2, let's put a comment in to that effect so that we can remember to scrub it when development on 3.2 starts.

I agree with this sentiment, but am happy to comply. I can add this comment if it is agreeable with @carltongibson

@jdufresne
Copy link
Member Author

I have restored the version check and overlay check in setup.py. I have also included a TODO comment to remove this at a future date. Thanks for all the feedback!

@felixxm
Copy link
Member

felixxm commented Nov 5, 2019

@jdufresne This check is not only for Python 2, you can try to install e.g. Django 3.0 with Python 3.5 via easy_install (it will raise two errors but finished properly) it can be harder to find small incompatibilities between Python 3.X versions. With our "unsupported" message you will get a clear information

Processing .
Writing django/setup.cfg
Running setup.py -q bdist_egg --dist-dir django/egg-dist-tmp-dxmgcrij

==========================
Unsupported Python version
==========================

This version of Django requires Python 3.6, but you're trying to
install it on Python 3.5.

This may be because you are using a version of pip that doesn't
understand the python_requires classifier. Make sure you
have pip >= 9.0 and setuptools >= 24.2, then try again:

    $ python -m pip install --upgrade pip setuptools
    $ python -m pip install django

This will install the latest version of Django which works on your
version of Python. If you can't upgrade your pip (or Python), request
an older version of Django:

    $ python -m pip install "django<2"
error: Setup script exited with 1

@jdufresne
Copy link
Member Author

easy_install is deprecated. Is that really an install method Django supports?

Here is setuptools purging it from their docs:

pypa/setuptools@4c0e204

I think we should do the same. It is only referenced in internals docs:

docs/internals/howto-release-django.txt
307  #. Test that the release packages install correctly using ``easy_install``
314          $ easy_install https://www.djangoproject.com/m/releases/$MAJOR_VERSION/Django-$RELEASE_VERSION.tar.gz

@ngnpope
Copy link
Member

ngnpope commented Nov 5, 2019

Why don't we punt on this action item then? This PR keeps the license exactly as it was before, no change. If there is a decision made that the license needs updating, that can easily happen after this lands (or even before, really). Whether or not we're using setup.cfg makes no difference on precising the license. Do we all agree?

Let's just consider at least changing BSD to BSD-3-Clause so that we are explicitly specifying the correct license. There are easily 10~20 variants. I'm fine to punt the dual license issue thing to be addressed later.

@ngnpope
Copy link
Member

ngnpope commented Nov 5, 2019

Taking a stripped down version of what Jon posted:

$ pip install .

...
  
  ==========================
  Unsupported Python version
  ==========================
  ...
  
      $ python -m pip install --upgrade pip setuptools
      $ python -m pip install django
  
  ...
  
      $ python -m pip install "django<2"
  ----------------------------------------

So this is Python 2 using pip install . which is then not telling us to download the correct source package - note we are installing from source, but to use pip install django to fetch it from PyPI after upgrading pip and setuptools. The purpose of that was to handle lack of support for python_requires in earlier versions of pip - hence following up with the request to use pip install django<2 if users are not able to upgrade pip. That message triggering in this way is actually sort of misleading.

I think we should do the same.

I would agree. I think it is fair to say that easy_install is explicitly not supported at this stage. The setuptools guys are even telling us it is broken in many ways. And again, from Felix's example, using easy_install on the wrong version of a source package with Python 3 tells you to upgrade pip and use pip to fetch from PyPI.

I'm honestly not sure that we're doing the right thing here for people using source packages. And if they're using new enough pip - which enough time should have passed ensuring that most people are - then python_requires will now work and be enough.

@carltongibson
Copy link
Member

OK, thanks for the comments all. I'll have another read over tomorrow and we can come to a final decision.

setup.cfg Outdated Show resolved Hide resolved
@carltongibson
Copy link
Member

carltongibson commented Nov 6, 2019

Right, lots going on here. I'm still reading about PEP 518 and such.

A couple of points thus far though:

Let's just consider at least changing BSD to BSD-3-Clause so that we are explicitly specifying the correct license. There are easily 10~20 variants. I'm fine to punt the dual license issue thing to be addressed later.

OK, but can we do that in a separate PR please? Should be easily mergeable and the commit history will show Clarified licence... when we're looking for it later.

Then, I would like us to keep the install warning, at least for now.

  • I accept the wording is sub-optimal, saying "download from pip" when you've tried to install from source, but we could improve that in another PR, and the headline news is correct:

      This version of Django requires Python 3.6, but you're trying to 
      install it on Python 3.5.
    
  • The easy_install discussion is not relevant — I can quite easily imagine someone still using easy_install — but you get the same issue using pip install . as any other method.

From your perspective, is this all about the user messaging?

Ultimately yes. I think there are many more users than we think about who don't have access to the latest Pythons, or even the option to always fetch from PyPI. I think if we can ensure clear error messages in these cases (and I know they're always going to be under-maintained...) then I think we do a good thing.

I'd just like a little more time to be 100% clear, but the scope of this is looking much more manageable. Thanks again for your efforts both.

@ngnpope
Copy link
Member

ngnpope commented Nov 6, 2019

OK, but can we do that in a separate PR please? Should be easily mergeable and the commit history will show Clarified licence... when we're looking for it later.

That's fair.

Then, I would like us to keep the install warning, at least for now.

Sure. Jon has essentially punted the removal to Django 4.0 by sticking a comment in.

  • The easy_install discussion is not relevant — I can quite easily imagine someone still using easy_install — but you get the same issue using pip install . as any other method.

Except pip will honor python_requires. It's not the end of the world as we've already decided to hold off until Django 4.0, by which time I imagine the whole story around packaging and the obsolescence of easy_install will have solidified. At least by 2022 it better have!

That is not to say that this cannot be done earlier, but at least it is on the radar for some point in the future.

(I should also add a point that I've missed up until now - it is nice for there to be no "arbitrary" code execution during package builds. Eventually it should also be possible to have a setup.cfg without a setup.py. This is somewhat less of a problem with wheels as the package is built prior to distribution, but source packages lead to execution of this code at install time to build the package... pip actually builds wheels now from the source package and then installs those.)

ngnpope added a commit to ngnpope/django that referenced this pull request Nov 6, 2019
Use the SPDX identifier in the license metadata field to clarify that
the primary license for Django is the BSD 3-Clause "New" or "Revised"
License. The 'License :: OSI Approved :: BSD License' trove classifier
is not clear enough to indicate which if the variants of the BSD license
Django uses.

See django#12013 (comment)
@ngnpope
Copy link
Member

ngnpope commented Nov 6, 2019

Should be easily mergeable and the commit history will show Clarified licence... when we're looking for it later.

Pull request for license clarification: #12038

@ubernostrum
Copy link
Member

On 5, first of all gulp. :) — I need to look into this. I'm going to ask @ubernostrum if it's something the board might be able to advise on?

@carltongibson I've read this and I'm still unsure what the question is. If someone can distill it plainly for me I'm happy to look into it.

@ngnpope
Copy link
Member

ngnpope commented Nov 7, 2019

@ubernostrum This stems from my #12013 (comment). Read that for more detail.

Essentially I noticed when reviewing this that the metadata field for the license was ambiguous. It is often enough to omit this field where it is already available in the trove classifiers. The classifier for "BSD License" isn't specific enough, however, as there are many variants and the value "BSD" in the license field isn't clear either. Hence pull request #12038. That I think is fairly straightforward.

So the main issue is that some of the code in Django is copied from the Python Standard Library. We used to have a backport of weakref, but now it is just a few functions in django.utils.http. (I will add a couple of comments in another PR to highlight some of those that do not make it clear.) When the weakref backport was added, the Python-2.0 license was added to the repository.

So the question is: Does this mean Django is released under the terms of both licenses?

If so, should we change the license metadata field to be (BSD-3-Clause AND Python-2.0) and add License :: OSI Approved :: Python Software Foundation License to classifiers?

Maybe I'm making something of nothing or opening a can of worms, but I felt this need clarification.

(@carltongibson A decision on this doesn't need to hold up this PR. We can address any action separately.)

@carltongibson carltongibson self-assigned this Nov 7, 2019
@carltongibson
Copy link
Member

carltongibson commented Nov 7, 2019

Look at this again now. I will rebase.

Copy link
Member

@carltongibson carltongibson left a comment

Choose a reason for hiding this comment

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

OK, thanks both. All still works. 🙂

Having talked to @felixxm, I've split into three commits. We'll backport the purely docs change.
I'll leave this to see if Mariusz has any comments, but looks good. Let's have it! 👍

@carltongibson carltongibson removed their assignment Nov 7, 2019
@jdufresne
Copy link
Member Author

I noticed the RemovedInDjango40Warning comment was removed from setup.py. Was that intentional?

@jdufresne
Copy link
Member Author

I believe for GitHub to pick up the Co-authored-by tag you'll need to remove the period at the end of the line. Notice that right now Nick's avatar isn't on the commit.

@carltongibson
Copy link
Member

Yes, it was, and . hmmm — good spot!

INSTALL Outdated Show resolved Hide resolved
jdufresne and others added 2 commits November 8, 2019 13:26
…up.py.

Co-authored-by: Nick Pope <nick.pope@flightdataservices.com>
…cfg.

Co-authored-by: Nick Pope <nick.pope@flightdataservices.com>
@felixxm
Copy link
Member

felixxm commented Nov 8, 2019

I made tests, few releases, installations etc., and I think we should merge the first two commits and drop the last one with pyproject.toml. Mainly because it impacts all installations from source by adding steps with extra requirements and building wheels:

Installing build dependencies ... done
Getting requirements to build wheel ... done
  Preparing wheel metadata ... done
...
Building wheels for collected packages: Django
  Building wheel for Django (PEP 517) ... done
  Stored in directory: /tmp/pip-ephem-wheel-cache-rdpid0sx/wheels/37/5a/91/c7c6aa11614143eb9df817cdae01d30967a46a1c3673469755
Successfully built Django
...

I don't see any advantages (in our case) of wheel over sdist 🤷‍♂️

@ngnpope
Copy link
Member

ngnpope commented Nov 8, 2019

So here is the documentation on how pip behaves which may explain better than I can, along with the previously linked articles.

I was trying to be conservative but misunderstood and thought that by not specifying build-backend we were opting out of the PEP 517 stuff which did have some issues with editable builds earlier this year. That amounted to the fact that editable installs were not specified under PEP 517 so some of the PyPA guys thought that it would be alright to prevent them - this didn't go down well because, while a new generic blessed way of doing them is as yet undefined, they worked. They backtracked on this though and there are no issues as far as I can tell. All of this would have only affected those who are developing Django as there is no reason to install it in editable mode if it is a dependency of your own project.

Given all that, perhaps we should just go with:

[build-system]
requires = ['setuptools>=40.8.0', 'wheel']
build-backend = 'setuptools.build_meta'

I've also notice that we'll need to add a line to tox.ini too - see here - which also highlights this as the recommended way to package.

So to my understanding, the whole purpose of this PEP 517 & 518 stuff is to formalise how packages are installed and, in the case of source packages, how those are built at install time without pip needing to know explicitly which build backend. In addition, any build time requirements are specified in pyproject.toml (setup_requires in setuptools has never worked well and is a bit chicken-and-egg) and pip will install those prior to starting the build. The source package is built as a wheel in an isolated virtualenv with all of the required build dependencies at the correct versions (something that was also questionable in the past) and that wheel is then (optionally) cached and installed. Note that pip has a single format to install from in the PEP 517 & 518 world.

As you say, it impacts all installations from source. This is intentional. Is it a negative impact however? Is it actually better because it is less error prone and reproduceable? Besides, how many installations are actually done from source distributions and not wheels these days?

If you decide to drop it be aware that this will come back again in the future.

@carltongibson
Copy link
Member

OK, thank again @pope1ni. We can go with the first two changes and pause to think (over the weekend say) about the last.

If you decide to drop it be aware that this will come back again in the future.

Yes. It will. That might be OK though: it might not be so hot then 🔥. Anyhow... we'll have a read. 👍

@ngnpope
Copy link
Member

ngnpope commented Nov 8, 2019

Let's not worry about the last commit for now.

It's not worth getting bogged down in at this stage. I honestly didn't think it would be that contentious or too soon, but I think the real problem here is a lack of clear messaging to developers around packaging in Python - what the roadmap is and how the changes all work.

Thank you for taking the time with this 🙃

@felixxm felixxm merged commit 85efc14 into django:master Nov 8, 2019
@jdufresne jdufresne deleted the django-setup-cfg branch November 8, 2019 20:17
@felixxm
Copy link
Member

felixxm commented Nov 8, 2019

Thanks y'all 🎉

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