# By the power of Grayskull... I have the Conda recipe! *The main goal of the Skeleto~~n~~r is to conquer Grayskull.* ## Introduction All jokes aside, the new project [grayskull :skull:](https://github.com/marcelotrevisani/grayskull) was created with the __intention__ of generating better Conda recipes that would allow to package properly projects available in different channels such as PyPI, CRAN, Conan, GitHub register, GitHub repositories and so on. On top of that, Grayskull is also being developed to help [conda-forge](https://conda-forge.org/) to update recipes. ## Current status Currently, Grayskull (version ``0.1.9``) is able to generate recipes just looking for packages on [PyPI](https://pypi.org/), and it is available on [PyPI](https://pypi.org/project/grayskull/) and [conda-forge](https://github.com/conda-forge/grayskull-feedstock). The GitHub repository for this package is: [https://github.com/marcelotrevisani/grayskull](https://github.com/marcelotrevisani/grayskull) Before Grayskull, we basically just had ``conda-build skeleton`` to generate recipes for Python packages on PyPI. In all other aspects, the difference of quality of the generated recipes, and also the time spent to generate them have a big discrepancy when compared to``conda-build skeleton`` and ``grayskull``. Grayskull generates recipes taking in consideration the platform, Python version available, selectors, compilers (Fortran, C and C++), packages constrains, license type, license file, and so forth. It uses metadata available from multiple sources to try to create the best recipe possible. ### Installation You can install ``grayskull`` using ``pip`` or ``conda``. #### With pip ``` pip install grayskull ``` #### With conda Grayskull is available on the conda-forge channel. ``` conda install -c conda-forge grayskull ``` ### Grayskull vs conda-build skeleton There are some differences of recipes generated by ``grayskull`` and ``conda skeleton``. Taking as example the ``pytest`` recipe, which has selectors for platforms, Python versions constrains, and has several packages constrains as well. #### Grayskull (0.1.9) - took 4 seconds to generate the recipe ```yaml {% set name = "pytest" %} {% set version = "5.3.5" %} package: name: {{ name|lower }} version: {{ version }} source: url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz sha256: 0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d build: number: 0 skip: true # [py2k] entry_points: - pytest=pytest:main - py.test=pytest:main script: {{ PYTHON }} -m pip install . -vv requirements: host: - pip - python - setuptools >=40.0 - setuptools_scm run: - atomicwrites >=1.0 # [win] - attrs >=17.4.0 - colorama # [win] - importlib-metadata >=0.12 # [py<38] - more-itertools >=4.0.0 - packaging - pathlib2 >=2.2.0 # [py<36] - pluggy <1.0,>=0.12 - py >=1.5.0 - python - wcwidth test: imports: - pytest commands: - pip check - pytest --help - py.test --help requires: - pip about: home: https://pypi.org/project/pytest/ summary: 'pytest: simple powerful testing with Python' dev_url: https://github.com/pytest-dev/pytest license: MIT license_file: LICENSE extra: recipe-maintainers: - marcelotrevisani ``` #### Skeleton (3.18.11) - took 31 seconds to generate the recipe ```yaml {% set name = "pytest" %} {% set version = "5.3.5" %} package: name: "{{ name|lower }}" version: "{{ version }}" source: url: "https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz" sha256: 0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d build: number: 0 script: "{{ PYTHON }} -m pip install . -vv" requirements: host: - atomicwrites >=1.0 - attrs >=17.4.0 - colorama;sys_platform =="win32" - importlib-metadata >=0.12 - more-itertools >=4.0.0 - packaging - pathlib2 >=2.2.0 - pip - pluggy >=0.12,<1.0 - py >=1.5.0 - python - wcwidth run: - atomicwrites >=1.0 - attrs >=17.4.0 - colorama;sys_platform =="win32" - importlib-metadata >=0.12 - more-itertools >=4.0.0 - packaging - pathlib2 >=2.2.0 - pluggy >=0.12,<1.0 - py >=1.5.0 - python - wcwidth about: home: The package home page license: MIT license_family: MIT license_file: summary: "pytest: simple powerful testing with Python" doc_url: dev_url: extra: recipe-maintainers: - your-github-id-here ``` #### Original recipe on ``conda-forge`` for ``pytest 5.3.5`` ```yaml {% set version = "5.3.5" %} package: name: pytest version: {{ version }} source: url: https://pypi.io/packages/source/p/pytest/pytest-{{ version }}.tar.gz sha256: 0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d build: skip: True # [py27] number: 1 script: "{{ PYTHON }} setup.py install --single-version-externally-managed --record record.txt" entry_points: - py.test = py.test:main - pytest = py.test:main requirements: host: - pip - python - setuptools >=40.0 - setuptools_scm run: - atomicwrites >=1.0 # [win] - attrs >=17.4.0 - colorama # [win] - importlib_metadata >=0.12 # [py<38] - more-itertools >=4.0 - packaging - pathlib2 >=2.2.0 # [py<36] - pluggy >=0.12,<1.0 - py >=1.5.0 - python - setuptools >=40.0 - wcwidth run_constrained: # pytest-faulthandler 2 is a dummy package. # if an older version of fault-handler is installed, it will conflict with pytest >=5. - pytest-faulthandler >=2 test: commands: - pytest -h imports: - pytest about: home: https://docs.pytest.org/en/latest/ license: MIT license_file: LICENSE summary: 'Simple and powerful testing with Python.' description: | The pytest framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries. doc_url: https://docs.pytest.org/en/latest/ dev_url: https://github.com/pytest-dev/pytest/ extra: recipe-maintainers: - flub - goanpeca - nicoddemus - ocefpaf - mingwandroid ``` #### Major differences | | Grayskull (0.1.9) | Skeleton (3.18.11) | |------------------:|:----------------------:|:---------------------------:| | **Command** | <span style="color: green;">grayskull pypi pytest</span> | <span style="color: green;">conda skeleton pypi pytest</span> | | **Time** | <span style="color: green;">4 seconds</span> | <span style="color: red;">31 seconds</span> | | **License** | <span style="color: green;">Added the license file and license type correctly</span> | <span style="color: brown;">Added just the license type</span> | | **Host Requirements** | <span style="color: green;">Added correctly all the host requirements</span> | <span style="color: red;">it didn't add the correctly the host dependencies. It added unnecessary dependencies and it is missing quite a few of them necessary to build the package</span> | | **Run Requirements** | <span style="color: green;">Missing just ``setuptools`` from the host requirements (but this dependency is not defined on ``pytest`` package)</span> | <span style="color: red;">Incorrectly dependencies added to the project, it will not build the recipe as well</span> | | **Selectors** | <span style="color: green;">Skipping correctly Python 2 and added selectors for windows and python versions</span> | <span style="color: red;">it didn't add any information regarding selectors</span> | | **Entry points** | <span style="color: green;">Added all entry points correctly</span> | <span style="color: red;">No entry points</span> | | **Does it build?** | <span style="color: green;">YES</span> | <span style="color: red;">NO</span> | In the case of ``noarch: python``, Grayskull is smart enough to detect when the recipe supports it, which is not done by Skeleton. It is important to highlight that Skeleton does not detect compilers as well. Nevertheless, Grayskull always try to detect it. ### Usage Grayskull (0.1.9) Project options: ``grayskull --help`` ``` usage: grayskull [-h] [--version] {pypi} ... Grayskull - Conda recipe generator positional arguments: {pypi} Options to generate PyPI recipes pypi Generate recipes based on PyPI optional arguments: -h, --help show this help message and exit --version, -v Print Grayskull version and exit ``` ``grayskull pypi --help`` ``` usage: grayskull pypi [-h] [--maintainers MAINTAINERS [MAINTAINERS ...]] [--output OUTPUT] pypi_packages [pypi_packages ...] positional arguments: pypi_packages Specify the PyPI package name. optional arguments: -h, --help show this help message and exit --maintainers MAINTAINERS [MAINTAINERS ...], -m MAINTAINERS [MAINTAINERS ...] List of maintainers which will be added to the recipe. --output OUTPUT, -o OUTPUT Path to where the recipe will be created ``` To generate the recipe you can just call ``grayskull`` and pass the channel (as for now we are just supporting PyPI, it should be pypi) and the package name. You should also specify an output folder using the option ``--output`` or ``-o`` and it will create the package folder, and the recipe in there. It is important to note that the user can specify a list of maintainers which will be added to the recipe using the option ``--maintainers``. Example for pytest: ![Grayskull CLI](https://raw.githubusercontent.com/marcelotrevisani/grayskull/master/docs/images/cli_example_grayskull.gif) If you need to specify the package version you can do it just puting the equal sign after the package name and the version just right after that. Example: ``` grayskull pypi requests=2.21.0 ``` or ``` grayskull pypi requests==2.21.0 ``` ![Grayskull pinned package - requests](https://raw.githubusercontent.com/marcelotrevisani/grayskull/master/docs/images/cli_example_grayskull_version.gif) If you want to generate multiple recipes just pass a list of packages, such as: ``` grayskul pypi pytest requests=2.21.0 colorama ``` ## Future plans * For the next major version (1.0.0) it is planned to add the functionality to be able to load the recipe and update just parts of it; * Generate Conda recipes using CRAN \(R\) channel (2.0.0); * Generate Conda recipes using Conan (C++) channel (3.0.0); ## Issues Any problem, question, suggestions please feel free to open an issue on the repository: https://github.com/marcelotrevisani/grayskull/issues Contributions are very welcome! :) --- [Marcelo Duarte Trevisani (@marcelotrevisani)](https://github.com/marcelotrevisani)