Giter VIP home page Giter VIP logo

poetry2conda's Introduction

poetry2conda

image

image

image

A script to convert a Python project declared on a pyproject.toml to a conda environment.

This is not an attempt to move away from pyproject.toml to conda. It is a tool to help teams maintain a single file for dependencies when there are collaborators that prefer regular Python/PyPI and others that prefer conda.

Features

  • Set conda channels for each dependency.
  • Rename conda dependencies.
  • Convert tilde and caret dependencies to regular version specifiers.
  • Handle pure pip dependencies.

Installation

You will be able to install poetry2conda by running:

$ pip install poetry2conda

Usage

The most straightforward use-case for poetry2conda is to convert a pyproject.toml that uses poetry. This can be achieved by adding the following section to your pyproject.toml:

[tool.poetry.dependencies]
foo = "^1.2.3"
# ...

[tool.poetry2conda]
name = "some-name-env"

Then, use the command line to create a conda environment file:

$ poetry2conda pyproject.toml environment.yaml

# or if you want to see the contents but not write the file:
$ poetry2conda pyproject.toml -

This will create a yaml file like:

name: some-name-env
dependencies:
  - foo>=1.2.3,<2.0.0
  # ...

If you want to include extras in the created environment, you can use the --extra or -E arguments. They can be used multiple times to specify multiple extras

If you also want to include development dependencies, the --dev argument will do that.

Sometimes, a dependency is handled differently on conda. For this case, the section tool.poetry2conda.dependencies can be used to inform on specific channels, or package names.

For example, if a dependency should be installed from a specific channel, like conda-forge, declare it as follows:

[tool.poetry.dependencies]
foo = "^1.2.3"
# ...

[tool.poetry2conda]
name = "my-env-with-channels"

[tool.poetry2conda.dependencies]
foo = { channel = "conda-forge" }

After conversion, the yaml file will look like:

name: my-env-with-channels
dependencies:
  - conda-forge::foo>=1.2.3,<2.0.0
  # ...

Sometimes, a package on PyPI does not have the same name on conda (why? why not? confusion!). For example, tables and pytables, docker and docker-py. To change the name when converting to a conda environment file, you can set it as:

[tool.poetry.dependencies]
docker = "^4.2.0"
# ...

[tool.poetry2conda]
name = "another-example"

[tool.poetry2conda.dependencies]
docker = { name = "docker-py" }

The converted yaml file will look like:

name: another-example
dependencies:
  - docker-py>=4.2.0,<5.0.0
  # ...

When a package does not exist on conda, declare it on the pip channel:

[tool.poetry.dependencies]
quetzal-client = "^0.5.2"
# ...

[tool.poetry2conda]
name = "example-with-pip"

[tool.poetry2conda.dependencies]
quetzal-client = { channel = "pip" }

Which would give:

name: example-with-pip
dependencies:
  - pip
  - pip:
    - quetzal-client>=0.5.2,<0.6.0

Not all poetry dependency types are supported, only regular ones and git dependencies:

[tool.poetry.dependencies]
my_private_lib = { git = "https://github.com/company/repo.git", tag = "v1.2.3" }
# ...

[tool.poetry2conda]
name = "example-with-git"

This is handled like a pure pip dependency:

name: example-with-git
dependencies:
  - pip
  - pip:
    - git+https://github.com/company/[email protected]#egg=my_private_lib

Packages with extras are supported on a pyproject.toml, but conda does not support extras. For the moment, this information is dropped:

[tool.poetry.dependencies]
dask = { extras = ["bag"], version = "^2.15.0" }
# ...

[tool.poetry2conda]
name = "example-with-extras"

Which will be translated to:

name: example-with-extras
dependencies:
  - dask>=2.15.0,<3.0.0

Sometimes (very rarely) a package is not available on PyPI but conda does have it. Poetry can handle this with a git dependency and poetry2conda can keep these as pip installable packages. But if you prefer to transform it to its conda package, use the following configuration:

[tool.poetry.dependencies]
weird = { git = "https://github.com/org/weird.git", tag = "v2.3" }

[tool.poetry2conda]
name = "strange-example"

[tool.poetry2conda.dependencies]
weird = { name = "bob", channel = "conda-forge", version = "^2.3" }  # You need to declare the version here

Which will be translated to:

name: strange-example
dependencies:
  - conda-forge::bob>=2.3.0,<3.0.0

Contribute

License

The project is licensed under the BSD license.

Why poetry2conda?

This part is an opinion.

Python is a great language with great libraries, but environment management has been notoriously bad. Bad enough to have its own XKCD comic:

Python environment bankrupcty.

There is a lack of agreement on how and where to declare dependencies. setup.py contains abstract dependencies (but only apply to packages), and requirements.txt file has concrete dependencies (with version specifications). But development dependencies go somewhere else in requirements-dev.txt and testing dependencies in requirements-test.txt. Because dependencies are now declared in two or more separate files, this is a burden. Some people read and parse requirements-*.txt files on their setup.py. Others say that this is a bad practice.

Then, there is the environment management problem. virtualenv was created a long time ago to isolate environments so you one does end up with the dependencies of another project. I do not know why, this was not enough, venv was created. And then some other ones that can handle different Python versions.

At some point on this story, a new generation of clever developers brought ideas from other package managers to improve on how packages, environments, etc. should be managed. requirements.txt were replaced (in theory) by Pipfile and Pipfile.lock. New tools were created to manage packages and environments, such as Pipenv and poetry, tackling even more problems such as virtual environments, Python versions, and many other distribution problems.

Dependencies, environemnts, package managers... this confused a lot of people (including me).

Eventually, I decided to give the PEP 5128 and poetry a try. It was not easy: a new markup language, TOML (Tom's Obvious Markup Language, which has this strange old man smell, like naphtalene, because it looks like a new INI file). I encountered many new problems with poetry. I abandoned many times but always came back because at least it helps me define my dependencies in only file. After two or three tries, I decided to migrate my code base to poetry and drop the requirement and setup files.

But wait...

To add a bit of entropy to the Python situation, a company called Continuum Analytics (later renamed Anaconda) created a different Python distribution and package management, Anaconda (and its less obese brother, Miniconda). I think they were tired of the current Python situation, and they were right. They replaced all of the virtual environment problems with their own environments and they distribute their own packages without using the current Python package authority, PyPI. This worked well, in my opinion, because Anaconda distributes compiled versions of some packages, giving massive performance improvements in some cases (like NumPy), because it is easier to setup on Windows, but more importantly because Anaconda was targeted for the scientific computing community (e.g. data scientists).

Cool! I should migrate to conda then! Alas, some people (like me), who used Python before Anaconda ever existed, tried it and got confused.

I have three main problems with conda: First, not all packages are distributed by Anaconda, so you eventually need to mix conda and pip to work together. It is difficult to summarize how many problems I have encountered when mixing these two. Second, every single day I use conda, I ran into problems: maybe something was installed on the root environment (this also happens without conda), maybe I wrote a command the wrong way (errors are often misleading), maybe the command syntax changed recently, maybe my network is slow and that explains why adding a new dependency takes ages (among other examples). I can go on. Third, I said to myself, if you are going to use conda, you should go all the way and write packages for their conda repositories. Oh boy, I tried that and it is very complicated and the documentation is so confusing. I eventually managed to do it, but I have PTSD.

So to summarize, I am not convinced by Anaconda, buy I have colleagues or collaborators that do use it. I don't understand why (yes, apparently tensorflow is faster with anaconda, sigh...). But I have to admit that conda is not going to go anywhere.

This leaves me in an uncomfortable situation: I want to use poetry, but I don't like forcing others to use it to. And by others I mean my conda friends. I searched for some tool to auto-convert from one to another. Dephell does this, but it does not address all of my use-cases. There is an open issue for some of them. I saw that changing dephell was going to be a complicated endeavor, so I decided to just write a new tool to do it.

So that's why poetry2conda exists.

poetry2conda's People

Contributors

abergeron avatar dojeda avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

poetry2conda's Issues

Conda only dependency

Hello!

I think this is a great tool, but I think there is one package that stops me from using it. I'm talking about rdkit.

You see, it's impossible to install it as a git dependency and the simplest way to install it is via Conda.

Can I use your tool in this case? And how 😊?)

KeyError on running poetry2conda

Following is the error

sumit@Sumits-MacBook-Air backend % poetry2conda pyproject.toml environment.yaml
Traceback (most recent call last):
  File "/opt/homebrew/bin/poetry2conda", line 8, in <module>
    sys.exit(main())
  File "/opt/homebrew/lib/python3.9/site-packages/poetry2conda/convert.py", line 294, in main
    converted_obj = convert(args.pyproject, include_dev=args.dev, extras=args.extras)
  File "/opt/homebrew/lib/python3.9/site-packages/poetry2conda/convert.py", line 53, in convert
    dependencies, pip_dependencies = collect_dependencies(
  File "/opt/homebrew/lib/python3.9/site-packages/poetry2conda/convert.py", line 161, in collect_dependencies
    tag = constraint["tag"]
KeyError: 'tag'

I'm suspecting this dependency in my pyproject.yaml is the culprit.

pyppeteer = { git = "https://github.com/pyppeteer/pyppeteer.git", rev = "0fd15a7" }

Looking for new maintainer

To anyone interested,
It has been a while since I updated this project and I currently don't have the bandwidth / time that this project requires.
I have seen that there are plenty of contributions and contributors so please let me know here if you are interested on becoming the maintainer of this project.

Option for extra indexes with Pip

I have a project that has a dependency for a package from an alternate source (not pypi). Is there a work around to to specify a package like that for pip?

Failing with dot-containing dependencies

Looks like dependencies with a dot aren't handled well, see below

[tool.poetry.dependencies]
python = "^3.6.1"
ruamel.yaml = "^0.16"

# dev dependencies ...
% poetry2conda --dev pyproject.toml environment.yml
Traceback (most recent call last):
  File "/home/vvk/miniconda3/envs/birdfeeder/bin/poetry2conda", line 8, in <module>
    sys.exit(main())
  File "/home/vvk/miniconda3/envs/birdfeeder/lib/python3.8/site-packages/poetry2conda/convert.py", line 295, in main
    converted_obj = convert(args.pyproject, include_dev=args.dev, extras=args.extras)
  File "/home/vvk/miniconda3/envs/birdfeeder/lib/python3.8/site-packages/poetry2conda/convert.py", line 53, in convert
    dependencies, pip_dependencies = collect_dependencies(
  File "/home/vvk/miniconda3/envs/birdfeeder/lib/python3.8/site-packages/poetry2conda/convert.py", line 167, in collect_dependencies
    raise ValueError(
ValueError: This converter only supports normal dependencies and git dependencies. No path, url, python restricted, environment markers or multiple constraints. In your case, check the "ruamel" dependency. Sorry.
(Pdb) poetry_dependencies.items()
dict_items([('python', '^3.6.1'), ('ruamel', {'yaml': '^0.16'}), ('poetry2conda', '^0.3'), ('pre-commit', '^2.2.0'), ('pytest', '^5.4'), ('pytest-cov', '^2.7'), ('pytest-mock', '^3.1.0')])

Install root package

Poetry by default will install your project’s package everytime you run install unless using the option --no-root. When exporting an environment.yaml and installing it to a conda virtual environment the root project package is not installed.

Also it is not possible to "sneak" this in as an optional, editable dependency or even a git dependency since Poetry is not happy about the self-loop.

semantic_version parsing fails with non semantic version constraints

It seems obvious, but when a constraing that does not follow semantic version is used on a dependency, poetry2conda will fail. Perhaps the most simple example is:

black = "^19.10b0"

which is present in our own pyproject.toml.

I think that there are two possible solutions here:

The pragmatic one, which is to catch semantic version parsing errors, show a warning and use pin what was declared. In the case of black = "^19.10b0", conda will not understand this caret version, but we could warn the use to not use a caret (black = "19.10b0") and we could pin it to black==19.10b0.

The more complex one: since poetry does understand this caret, we should probably re-use poetry caret-handling code. This is complex because we don't want to set poetry as a dependency (it would install poetry on the virtual environment, and this is discouraged on poetry's installation guide). However, I saw that poetry has recently been reorganized on a core package, which may respond to our need.

Empty file created when poetry2conda fails

When the poetry2conda command fails, the target environment.yaml file is still created with empty contents. This is an undesirable behavior due to the usage of argparse.FileType. Ideally, the file should be written only when the conversion has succeeded.

Question: Why not take the environment name from `tool.poetry.name`?

Hi,

Thanks for this tool that makes it really easy to keep my conda friends happy!

I don't really understand why I have to add an extra section to my pyproject.toml file, as in general I would personally want the environment to be named after the project, and to be warned in case I already have an environment with that name.
If I don't want it to have to same name, I could just change the environment name in the resulting environment.yml file, right?
What am I missing?

Thanks in advance for the response!

convert poetry sources to pip --index-url

Hi, first of all many thanks for this project!

TL;DR: the generated pip section should include the --index-url and --extra-index-url using info from tool.poetry.source

I'm on a limbo of mixed environments and repos, but I need to switch back from a poetry-based to a conda-based build env.
I already have some projects built with poetry and saved on a private devpi index;
the directive to use the private repo is in pyproject.toml's tool.poetry.source:

[[tool.poetry.source]]
name = "private"
url = "https://my.private.repo/my/stable/+simple/"
default= true

I added the tool.poetry2conda.dependencies section to mark my packages with channel = "pip", and when I use poetry2conda pyproject.toml environment.yml, the yaml file contains the pip packages but no --index-url option.

Passing extra options in the environment.yml's pip section is possible from conda version 4.4.0 (see this pr).

It would be wonderful if the urls specified in the tool.poetry.source sections could be translated to a --index-url entry if default = true, or a --extra-index-url otherwhise.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.