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

Reduce manual pre-installation requirements via pyproject.toml #162

Merged
merged 6 commits into from
Dec 10, 2022

Conversation

nrontsis
Copy link
Contributor

@nrontsis nrontsis commented Dec 9, 2022

Adds pyproject.toml file to specify the build dependencies for this package. This removes the need to have e.g. setuptools before running pip install cyipopt.

Docs are updated accordingly.

On a related note, I think it's worth considering removing requirements.txt to reduce confusion.

@nrontsis nrontsis marked this pull request as ready for review December 9, 2022 13:36
@nrontsis nrontsis changed the title Add pyproject.toml Reduce manual pre-installation requirements via pyproject.toml Dec 9, 2022
@moorepants
Copy link
Collaborator

I'm willing to add this file if there aren't negative effects, but regardless can we leave the build deps in the docs. I'd like python setup.py install to work as long as it can. I've noticed that the presence of the pyproject.toml file causes difficulties in installation in downstream packaging, for example in conda-forge, which has it's own build specification file. I've historically had to delete that file when packaging for conda forge, but I'm not up on the current state of affairs. See: conda-forge/conda-forge.github.io#1174

@moorepants
Copy link
Collaborator

It is also worth noting that pyproject.toml does not handle build dependencies that can't be installed via pypi/pip, e.g. ipopt.

@moorepants
Copy link
Collaborator

@brocksam are you up on the info about this? I only have negative experiences with it, but I recognize it is likely our destiny.

* setuptools
* cython
* numpy
* scipy [optional]
Copy link
Collaborator

Choose a reason for hiding this comment

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

You've also removed scipy here as an optional dep. How does pyproject.toml handle that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My understanding is that the scipy interface of cyipopt is not related to the build process, so it has no place in build requiremetns.

Copy link
Collaborator

Choose a reason for hiding this comment

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

This list is a list of all build and run time dependencies (optional or not). I'd like to leave the list intact in the documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have restored the diff here, but I find that docs confusing. They say:

To begin installing from source you will need to install the following dependencies

So I don't understand them to be "build or run time depedencies" necessarily.

Furthermore, no versions are specified here, which is inconsistent with what setup.py requires.

@moorepants
Copy link
Collaborator

On a related note, I think it's worth considering removing requirements.txt to reduce confusion.

This is currently used in the CI setup and maybe even readthedocs.

@nrontsis
Copy link
Contributor Author

nrontsis commented Dec 9, 2022

On a related note, I think it's worth considering removing requirements.txt to reduce confusion.

This is currently used in the CI setup and maybe even readthedocs.

Yeah, I saw that. My opinion would be that it's best that the requirements of CI etc are better to be specified "closer" to the CI code, as the do for example here. Either way, it's beyond the scope of this PR.

@nrontsis
Copy link
Contributor Author

nrontsis commented Dec 9, 2022

can we leave the build deps in the docs. I'd like python setup.py install to work as long as it can

How about I instead write that "all the pypi requirements specified in pyproject.toml are also required", in contrast to listing them again?

@moorepants
Copy link
Collaborator

There are relevant older conversations on this, e.g. #100

@nrontsis
Copy link
Contributor Author

nrontsis commented Dec 10, 2022

There are relevant older conversations on this, e.g. #100

After reading this, I think we might have different workflows in mind. For example, I don't see the following as a reason to not try to eliminate custom python pre-build dependencies:

we will always be left with preinstallation statements like this because all build dependencies can't be installed via pypi.

In workflows I work with, all python dependencies are resolved via a python package manager (like poetry) and system level dependencies (of several languages) are handled with separate tools within Docker.

Having cyipopt require setuptools or numpy before it's even attempted to be installed breaks the dependency resolution of e.g. poetry, and requires custom hacks in our Docker files. Currently this is our only dependency that creates this kind of issues among many other python packages, including several other optimisation solvers - but maybe issues like these are hidden in other packages because the might release pre-built wheels.

@moorepants
Copy link
Collaborator

moorepants commented Dec 10, 2022

We should try to make this package work with new popular packaging tools, but we also need to make sure it continues to work with our currently supported packaging methods. This package predates poetry, wheels, and such. It is also traditionally has been a challenge to package due to a the dependencies (build and run) that are not available via pypi. A far as I understand, poetry still can't install this package fully because it can't install all of the necessary dependencies.

If adding the pyproject file helps this work with poetry, we can include it, but we can't break the existing setup.

@moorepants
Copy link
Collaborator

but maybe issues like these are hidden in other packages because the might release pre-built wheels

See #41. If you can point us to a way to build wheels for the major operating systems in a way that works correctly with non-wheel ipopt installations, then we can adopt that here. I'm not aware of what that solution is.

@nrontsis
Copy link
Contributor Author

nrontsis commented Dec 10, 2022

A far as I understand, poetry still can't install this package fully because it can't install all of the necessary dependencies.

Do you have an example poetry configuration in mind?

Here's what I would use that works with the patch of this PR:

Dockerfile:

FROM python:3.10-slim
WORKDIR /app

RUN apt-get update && \
apt-get install -y build-essential coinor-libipopt-dev liblapack-dev pkg-config

RUN pip install -U pip && pip install poetry

RUN poetry new .
RUN poetry add git+https://github.com/nrontsis/cyipopt.git@patch-2  # Using poetry add cyipopt fails currently

# Download example problem
RUN apt-get install -y curl
RUN curl -O https://raw.githubusercontent.com/nrontsis/cyipopt/master/examples/hs071.py

Build and solve example problem:

docker build . --tag cyipopt
docker run cyipopt poetry run python hs071.py

If adding the pyproject file helps this work with poetry, we can include it, but we can't break the existing setup.

Sure. Do you have an minimal example setup that shows pyproject breaking things? Or some things you want to see in this PR for de-risking?

If you can point us to a way to build wheels for the major operating systems in a way that works correctly with non-wheel ipopt installations, then we can adopt that here.

I saw you opened #163 for this. I am not experienced on this, but I would look at the python interfaces of osqp, cvxpy and casadi to see how they generate their binaries.

Also in my opinion having pre-built wheels for some architectures does not remove the need for this PR, as users might still want to compile themselves for some reason (for example running in a another architecture like Linux aarch64).

@moorepants
Copy link
Collaborator

Do you have an example poetry configuration in mind?

No, I don't personally use poetry for anything. This package has to work with a variety of downstream packaging or build tools, poetry is just one of them.

Your example dockerfile that uses poetry works only on Debian based systems and require an apt-get installed ipopt. Yes, installing from source works cleanly (now) on Debian systems. This is explained in the install docs already, e.g.

apt install build-essential pkg-config python-dev cython python-numpy coinor-libipopt1v5 coinor-libipopt-dev
python -m pip install cyipopt

Has worked since Ubuntu 18.04 gained functional ipopt binaries.

We also support Mac and Windows and other linux distros.

# Using poetry add cyipopt fails currently

Can you report this error message here?

Sure. Do you have an minimal example setup that shows pyproject breaking things? Or some things you want to see in this PR for de-risking?

All these issues discuss various problems:

conda-forge/conda-forge.github.io#1174
conda-forge/conda-forge.github.io#1540
conda-forge/conda-forge.github.io#1778

There are numerous conda packages that have to delete the pyproject.toml file before building. This one would likely have to do the same. So, if we package this pyproject.toml file in the source distribution, then we will have to resolve these downstream effects.

I saw you opened #163 for this. I am not experienced on this, but I would look at the python interfaces of osqp, cvxpy and casadi to see how they generate their binaries.

I've been reading these things all morning. It's a bit nasty to get working and will take some time, but at least I see that is it now possible and we can likely bundle ipopt in a wheel for all platforms.

Also in my opinion having pre-built wheels for some architectures does not remove the need for this PR

I am open to adding this file specifically for poetry to function, but it is likely going to break the conda builds. I'm trying to understand if there are better ways to handle the incompatibilities. This project currently supports installing from conda, setup.py, and pip. We can add poetry support if we retain the currently supported methods.

@nrontsis
Copy link
Contributor Author

Using poetry add cyipopt fails currently

Can you report this error message here?

Here's what I got
 => ERROR [6/8] RUN poetry add cyipopt                                                                                                               14.1s
------
 > [6/8] RUN poetry add cyipopt:
#9 0.869 Creating virtualenv app-9TtSrW0h-py3.10 in /root/.cache/pypoetry/virtualenvs
#9 1.638 Using version ^1.2.0 for cyipopt
#9 1.640
#9 1.640 Updating dependencies
#9 1.641 Resolving dependencies...
#9 5.387
#9 5.387 Writing lock file
#9 5.464
#9 5.464 Package operations: 3 installs, 0 updates, 0 removals
#9 5.464
#9 5.466   • Installing cython (0.29.32)
#9 5.467   • Installing numpy (1.23.5)
#9 11.00   • Installing cyipopt (1.2.0)
#9 13.54
#9 13.54   CalledProcessError
#9 13.54
#9 13.54   Command '['/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10/bin/python', '-m', 'pip', 'install', '--use-pep517', '--disable-pip-version-check', '--isolated', '--no-input', '--prefix', '/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10', '--no-deps', '/root/.cache/pypoetry/artifacts/a5/a8/9e/80306f8dda7dc4682a86897b5dfdc4d1c4444d5055260091388599b8ec/cyipopt-1.2.0.tar.gz']' returned non-zero exit status 1.
#9 13.54
#9 13.54   at /usr/local/lib/python3.10/subprocess.py:526 in run
#9 13.63        522│             # We don't call process.wait() as .__exit__ does that for us.
#9 13.63        523│             raise
#9 13.63        524│         retcode = process.poll()
#9 13.63        525│         if check and retcode:
#9 13.63     →  526│             raise CalledProcessError(retcode, process.args,
#9 13.63        527│                                      output=stdout, stderr=stderr)
#9 13.63        528│     return CompletedProcess(process.args, retcode, stdout, stderr)
#9 13.63        529│
#9 13.63        530│
#9 13.63
#9 13.63 The following error occurred when trying to handle this error:
#9 13.63
#9 13.63
#9 13.63   EnvCommandError
#9 13.63
#9 13.63   Command ['/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10/bin/python', '-m', 'pip', 'install', '--use-pep517', '--disable-pip-version-check', '--isolated', '--no-input', '--prefix', '/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10', '--no-deps', '/root/.cache/pypoetry/artifacts/a5/a8/9e/80306f8dda7dc4682a86897b5dfdc4d1c4444d5055260091388599b8ec/cyipopt-1.2.0.tar.gz'] errored with the following return code 1, and output:
#9 13.63   Processing /root/.cache/pypoetry/artifacts/a5/a8/9e/80306f8dda7dc4682a86897b5dfdc4d1c4444d5055260091388599b8ec/cyipopt-1.2.0.tar.gz
#9 13.63     Installing build dependencies: started
#9 13.63     Installing build dependencies: finished with status 'done'
#9 13.63     Getting requirements to build wheel: started
#9 13.63     Getting requirements to build wheel: finished with status 'error'
#9 13.63     error: subprocess-exited-with-error
#9 13.63
#9 13.63     × Getting requirements to build wheel did not run successfully.
#9 13.63     │ exit code: 1
#9 13.63     ╰─> [41 lines of output]
#9 13.63         /tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/setuptools/installer.py:27: SetuptoolsDeprecationWarning: setuptools.installer is deprecated. Requirements should be satisfied by a PEP 517 installer.
#9 13.63           warnings.warn(
#9 13.63         /root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10/bin/python: No module named pip
#9 13.63         Traceback (most recent call last):
#9 13.63           File "/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/setuptools/installer.py", line 82, in fetch_build_egg
#9 13.63             subprocess.check_call(cmd)
#9 13.63           File "/usr/local/lib/python3.10/subprocess.py", line 369, in check_call
#9 13.63             raise CalledProcessError(retcode, cmd)
#9 13.63         subprocess.CalledProcessError: Command '['/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10/bin/python', '-m', 'pip', '--disable-pip-version-check', 'wheel', '--no-deps', '-w', '/tmp/tmpxmaz4k18', '--quiet', 'numpy>=1.15']' returned non-zero exit status 1.
#9 13.63
#9 13.63         The above exception was the direct cause of the following exception:
#9 13.63
#9 13.63         Traceback (most recent call last):
#9 13.63           File "/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 351, in <module>
#9 13.63             main()
#9 13.63           File "/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 333, in main
#9 13.63             json_out['return_val'] = hook(**hook_input['kwargs'])
#9 13.63           File "/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10/lib/python3.10/site-packages/pip/_vendor/pep517/in_process/_in_process.py", line 118, in get_requires_for_build_wheel
#9 13.63             return hook(config_settings)
#9 13.63           File "/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/setuptools/build_meta.py", line 338, in get_requires_for_build_wheel
#9 13.63             return self._get_build_requires(config_settings, requirements=['wheel'])
#9 13.63           File "/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/setuptools/build_meta.py", line 320, in _get_build_requires
#9 13.63             self.run_setup()
#9 13.63           File "/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/setuptools/build_meta.py", line 484, in run_setup
#9 13.63             super(_BuildMetaLegacyBackend,
#9 13.63           File "/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/setuptools/build_meta.py", line 335, in run_setup
#9 13.63             exec(code, locals())
#9 13.63           File "<string>", line 26, in <module>
#9 13.63           File "/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/setuptools/dist.py", line 874, in fetch_build_eggs
#9 13.63             resolved_dists = pkg_resources.working_set.resolve(
#9 13.63           File "/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/pkg_resources/__init__.py", line 789, in resolve
#9 13.63             dist = best[req.key] = env.best_match(
#9 13.63           File "/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/pkg_resources/__init__.py", line 1075, in best_match
#9 13.63             return self.obtain(req, installer)
#9 13.63           File "/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/pkg_resources/__init__.py", line 1087, in obtain
#9 13.63             return installer(requirement)
#9 13.63           File "/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/setuptools/dist.py", line 944, in fetch_build_egg
#9 13.63             return fetch_build_egg(self, req)
#9 13.63           File "/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/setuptools/installer.py", line 84, in fetch_build_egg
#9 13.63             raise DistutilsError(str(e)) from e
#9 13.63         distutils.errors.DistutilsError: Command '['/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10/bin/python', '-m', 'pip', '--disable-pip-version-check', 'wheel', '--no-deps', '-w', '/tmp/tmpxmaz4k18', '--quiet', 'numpy>=1.15']' returned non-zero exit status 1.
#9 13.63         [end of output]
#9 13.63
#9 13.63     note: This error originates from a subprocess, and is likely not a problem with pip.
#9 13.63   error: subprocess-exited-with-error
#9 13.63
#9 13.63   × Getting requirements to build wheel did not run successfully.
#9 13.63   │ exit code: 1
#9 13.63   ╰─> See above for output.
#9 13.63
#9 13.63   note: This error originates from a subprocess, and is likely not a problem with pip.
#9 13.63
#9 13.63
#9 13.63   at /usr/local/lib/python3.10/site-packages/poetry/utils/env.py:1540 in _run
#9 13.71       1536│                 output = subprocess.check_output(
#9 13.71       1537│                     command, stderr=subprocess.STDOUT, env=env, **kwargs
#9 13.71       1538│                 )
#9 13.71       1539│         except CalledProcessError as e:
#9 13.71     → 1540│             raise EnvCommandError(e, input=input_)
#9 13.71       1541│
#9 13.71       1542│         return decode(output)
#9 13.71       1543│
#9 13.71       1544│     def execute(self, bin: str, *args: str, **kwargs: Any) -> int:
#9 13.71
#9 13.71 The following error occurred when trying to handle this error:
#9 13.71
#9 13.71
#9 13.71   PoetryException
#9 13.71
#9 13.71   Failed to install /root/.cache/pypoetry/artifacts/a5/a8/9e/80306f8dda7dc4682a86897b5dfdc4d1c4444d5055260091388599b8ec/cyipopt-1.2.0.tar.gz
#9 13.71
#9 13.71   at /usr/local/lib/python3.10/site-packages/poetry/utils/pip.py:58 in pip_install
#9 13.71        54│
#9 13.71        55│     try:
#9 13.71        56│         return environment.run_pip(*args)
#9 13.71        57│     except EnvCommandError as e:
#9 13.71     →  58│         raise PoetryException(f"Failed to install {path.as_posix()}") from e
#9 13.71        59│
#9 13.71
------
executor failed running [/bin/sh -c poetry add cyipopt]: exit code: 1

Your example dockerfile that uses poetry works only on Debian based systems and require an apt-get installed ipopt

The point I was trying to make is that assuming you have non-python system-level dependencies installed, poetry will work with the fix of this PR. The same point holds for other OSes, I believe; I can provide example scripts if you want.

This is explained in the install docs already, e.g.

apt install build-essential pkg-config python-dev cython python-numpy coinor-libipopt1v5 coinor-libipopt-dev
python -m pip install cyipopt

Has worked since Ubuntu 18.04 gained functional ipopt binaries.

The problem with the above approach is that a certain version of numpy (and the other python packages you install via apt-get) is used that might not be compatible with the requirements of a wider python project. In the example I have listed in my previous comment poetry would find a version for numpy that satisfies all of the project's requirements, if such a version exists. Instead, using the above will just use whatever numpy apt-get gives. numpy incompatibilities like these in cyipopt have caused segfaults to me in the past.

Similar complications exist for non-python system level dependencies, but I don't see these as a reason to not handle python dependency resolution as best as possible. Which in this case includes specifying as best as possible the python build dependencies.

This project currently supports installing from conda, setup.py, and pip.

As I described above, I don't think that pip is supported properly.

All these issues discuss various problems
...

Thanks I will take a look at these.

@moorepants
Copy link
Collaborator

Here are key pieces of info from your error message:

Command '['/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10/bin/python', '-m', 'pip', 'install', '--use-pep517', '--disable-pip-version-check', '--isolated', '--no-input', '--prefix', '/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10', '--no-deps', '/root/.cache/pypoetry/artifacts/a5/a8/9e/80306f8dda7dc4682a86897b5dfdc4d1c4444d5055260091388599b8ec/cyipopt-1.2.0.tar.gz']' returned non-zero exit status 1.
/tmp/pip-build-env-h9al84rn/overlay/lib/python3.10/site-packages/setuptools/installer.py:27: SetuptoolsDeprecationWarning: setuptools.installer is deprecated. Requirements should be satisfied by a PEP 517 installer.
subprocess.CalledProcessError: Command '['/root/.cache/pypoetry/virtualenvs/app-9TtSrW0h-py3.10/bin/python', '-m', 'pip', '--disable-pip-version-check', 'wheel', '--no-deps', '-w', '/tmp/tmpxmaz4k18', '--quiet', 'numpy>=1.15']' returned non-zero exit status 1.

It looks like poetry invokes a very specific set of flags to pip.

These are the flags:

  --isolated                  Run pip in an isolated mode, ignoring environment variables and user configuration.
  --no-deps                   Don't install package dependencies.
  --use-pep517                Use PEP 517 for building source distributions (use --no-use-pep517 to force legacy behaviour).
  --disable-pip-version-check    Don't periodically check PyPI to determine whether a new version of pip is available for download. Implied with --no-index.

If poetry is disabling dependency installation, then it is expected to manually install the dependencies before invoking the install command. We also don't yet support PEP-517. One reason being that it breaks the conda builds. I haven't updated myself on the latest state of PEP-517 + conda builds, maybe there is now a compatible way to manage it. If there is, then we can adopt it for this package and add poetry support. I believe the package installs fine with pip if the no-deps and use-pep517 flags are not invoked, which is pip's long time default behavior.

The deprecation warning is concerning though. It looks like the use of setup.py may be removed in the future. We'll be force to migrate at some point given that.

The scenario for this package is:

  1. we support conda installs as the best option for cross platform, simplest installation
  2. these pep 517 changes don't yet have a good solution for conda build compatibility
  3. pip/poetry/other python package managers can't, in general, install and manage non-python dependencies

So there is a bit of a catch 22 until we learn how all this works together.

Some things that will help:

  • Somone makes cross platform ipopt wheels
  • We get cross platform cyipopt wheels with ipopt bundle working
  • conda build supports PEP 517 style installations

For now, we can drop this pyproject.toml in the repository, but there needs to be more investigation before we add it to the source distribution.

@moorepants
Copy link
Collaborator

The problem with the above approach is that a certain version of numpy (and the other python packages you install via apt-get) is used that might not be compatible with the requirements of a wider python project.

You can install cyipopt alongside any other set of python dependencies as long as the numpy, cython, ipopt, and scipy version are above the minimally supported versions. This works in a virtual env or conda env, for example. Of course, the non-python deps need to be installed in some way other than with pip. So something like:

apt install ipopt
poetry setup_my_big_envrionment_including_cyiopt_deps
activate my_big_env
pip install --no-deps cyipopt

Or if poetry has a flag to allow non-pep 517 compliant packages, that could be invoked.

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

Successfully merging this pull request may close these issues.

2 participants