How to convert a conda env yaml file to a list of requirements for a settings.ini file accounting for channels and conversions for pypi

huangapple go评论103阅读模式
英文:

How to convert a conda env yaml file to a list of requirements for a settings.ini file accounting for channels and conversions for pypi

问题

Motivation

我使用并喜欢nbdev。它使得逐步开发一个Python包变得非常容易。尤其是因为我通常会在使用该包的项目旁边开发它。这个问题是否需要了解nbdev不需要。它只是解释了我为什么提出这个问题。通常情况下,当我创建一个新的nbdev项目(nbdev_new)时,我会得到一个settings.ini文件和一个setup.py文件。为了使在不同的包/项目上继续工作变得简单,我会立即为项目创建一个conda环境文件 env.yml(参见文件 > 示例Conda文件[也许这个链接有效][示例环境文件])。

我知道开发包的环境不一定是它所需的最低依赖关系。此外,一个包的依赖关系是使用该包进行项目工作时可能需要的那些依赖关系的子集。在我的用例中,我很明确地双重使用了它!我在项目上使用它时正在开发包。

因此,出于这个问题的考虑,让我们假设包依赖关系 == 项目依赖关系。换句话说,env.yml文件包含了settings.ini文件的所有requirements

nbdev工作流程

  1. 创建一个新的空仓库“current_project”并克隆它

  2. cd path/to/current_project

  3. nbdev_new

  4. 创建env.yml文件

  5. 创建/更新环境:

  1. # 创建conda环境
  2. $ mamba env create -f env.yml
  3. # 根据需要更新conda环境
  4. $ mamba env update -n current_project --file env.yml
  5. # $ mamba env update -n current_project --file env.mac.yml
  1. 激活环境:
  1. # 激活conda环境
  2. $ conda activate current_project
  1. 安装current_project
  1. # 用于本地开发安装
  2. $ pip install -e .

问题阐述

我正在使用setup.py文件开发一个Python包。我的包可能有依赖关系(在settings.ini下的键requirements中列出),这些依赖关系会自动导入并在setup.py文件中使用。在开发我的包时,我有一个conda环境,该环境在一个名为env.yml的yaml文件中指定(参见文件 > 示例Conda文件[也许这个链接有效][示例环境文件])。

我还有一些GitHub操作,用于测试我的包。我不喜欢手动更新settings.ini(尤其是因为它不允许多行),以将要求导入setup.py中。尤其是因为我已经在我的env.yml文件中将它们列得很漂亮。所以我的问题如下:

问题

> 鉴于一个conda环境yaml文件(例如env.yml),如何迭代其内容并将依赖关系(和版本)转换为正确的pypi版本(setup.py所需),并将它们存储在settings.ini中的关键字requirements下?

注意

  • conda中的版本规范要求与pypi不同。特别是===等等。
  • conda的包名称可能与pypi不同。例如,pytorchpypi上列为torch,而在conda中列为pytorch
  • 环境yaml文件可能包含渠道说明符,例如conda-forge::<package-name>
  • 环境yaml文件可能指定Python版本,例如python>=3.10,这不应该是一个要求。
  • 我的理想解决方案适用于我的工作流程。这意味着env.yml的内容需要传递到settings.ini中。

期望的结果

我的期望结果是,我可以将所有包要求存储在conda环境文件env.yml中,并自动将它们找到并放入setup.py文件的install_requires中。由于我的工作流程是围绕从settings.ini文件(来自nbdev)中读取要求来构建的,所以我希望解决方案能够获取env.yml的值并将它们放入settings.ini中。

注意:我会在下面的答案中分享我的当前解决方案。请帮助/改进它!

文件

示例conda文件

  1. # 示例YAML文件
  2. name: current_project
  3. channels:
  4. - pytorch
  5. - conda-forge
  6. - fastai
  7. dependencies:
  8. - python>=3.10
  9. # 实用工具
  10. # -------------------------------------------------------------------------
  11. - tqdm
  12. - rich
  13. - typer
  14. # Jupyter Notebook
  15. # -------------------------------------------------------------------------
  16. - conda-forge::notebook
  17. - conda-forge::ipykernel
  18. - conda-forge::ipywidgets
  19. - conda-forge::jupyter_contrib_nbextensions
  20. # nbdev
  21. # -------------------------------------------------------------------------
  22. - fastai::nbdev>=2.3.12
  23. # PyTorch &深度学习
  24. # -------------------------------------------------------------------------
  25. - pytorch>=2
  26. # 注意:如果使用支持CUDA的GPU,添加pytorch-cuda。如果您使用的是Apple Silicon,则需要删除此项
  27. # - pytorch::pytorch-cuda
  28. - conda-forge::pytorch-lightning
  29. # 绘图
  30. # -------------------------------------------------------------------------
  31. - conda-forge::matplotlib
  32. - conda-forge::seaborn
  33. # 数据整理
  34. # -------------------------------------------------------------------------
  35. - conda-forge::scikit
  36. <details>
  37. <summary>英文:</summary>
  38. **NOTE**: you can start at [Problem formulation][problem formulation]. The motivation section just explains how I found myself asking this.
  39. # Motivation
  40. I use and love [nbdev]. It makes developing a python package iteratively as easy as it gets. Especially, as I generally do this along side a project which uses said package. Does this question require knowledge of [nbdev]? **no**. It only motivates why I am asking. Normally when I create a new [nbdev] project (`nbdev_new`), I get a `settings.ini` file and `setup.py` file. In order to keep working on different packages / projects simple, I will immediately create a [conda environment file][conda yml] `env.yml` for the project (see `Files &gt; Example Conda File` [maybe this link works][example env file]).
  41. I know that the environment one develops a package in is **NOT** necessarily the minimum dependencies it requires. Further, a package&#39;s dependencies are a subset of those one may _need_ for a working on a project utilizing said package. In **MY** use case it is clear that I am double-dipping! I am *developing* the package as I _use_ it on a project.
  42. So for the sake of this question let&#39;s assume that the `package dependencies == project dependencies`. In other words, the `env.yml` file contains all of the `requirements` for the `setting.ini` file.
  43. ## nbdev workflow
  44. 1. make new empty repo &quot;current_project&quot; and clone it
  45. 2. `cd path/to/current_project`
  46. 3. `nbdev_new`
  47. 4. make `env.yml` file
  48. 5. create / update environment:
  49. ```sh
  50. # create conda environment
  51. $ mamba env create -f env.yml
  52. # update conda environment as needed
  53. $ mamba env update -n current_project --file env.yml
  54. # $ mamba env update -n current_project --file env.mac.yml
  1. active environment:
  1. # activate conda environment
  2. $ conda activate current_project
  1. install current_project:
  1. # install for local development
  2. $ pip install -e .

Problem formulation

I am developing a package in python using a setup.py file. My package may have requirements (listed under settings.ini with the key requirements) that get automatically important and used in the setup.py file. While developing my package I have a conda environment which is specified in a yaml file env.yml (see Files &gt; Example Conda File maybe this link works).

I also have some GitHub actions that test my package. I dislike having to update settings.ini manually (especially since it doesn't allow for multiple lines) to get the requirements into setup.py. Especially as I have already listed them out nice and neatly in my env.yml file. So my question is as follows:

Question

> Given a conda environment yaml file (e.g. env.yml) how can one iterate through its content and convert the dependencies (and the versions) to the correct pypi version (required by setup.py), storing them in settings.ini under the keyword requirements?

caveats:

  • version specifier requirements in conda are not the same as pypi. Most notably = vs ==, amongst others.
  • package names for conda may not be the same for pypi. For example pytorch is listed as torch for pypi and pytorch for conda.
  • the environment yaml file may have channel specifiers e.g. conda-forge::&lt;package-name&gt;
  • the environment yaml file may specify the python version e.g. python&gt;=3.10, which shouldn't be a requirement.
  • MY ideal solution works with my workflow. That means the contents of env.yml need to get transferred to settings.ini.

Desired outcome

My desired outcome is that I can store all of my package requirements in the conda environment file env.yml and have them automatically find themselves in the setup.py file under install_requires. Since my workflow is built around reading the requirements in from a settings.ini file (from nbdev), I would like the solution to take the values of env.yml and put them in settings.ini.

Note I am sharing my current solution as an answer below. Please help / improve it!

Files

Example conda file

  1. # EXAMPLE YAML FILE
  2. name: current_project
  3. channels:
  4. - pytorch
  5. - conda-forge
  6. - fastai
  7. dependencies:
  8. - python&gt;=3.10
  9. # Utilities
  10. # -------------------------------------------------------------------------
  11. - tqdm
  12. - rich
  13. - typer
  14. # Jupyter Notebook
  15. # -------------------------------------------------------------------------
  16. - conda-forge::notebook
  17. - conda-forge::ipykernel
  18. - conda-forge::ipywidgets
  19. - conda-forge::jupyter_contrib_nbextensions
  20. # nbdev
  21. # -------------------------------------------------------------------------
  22. - fastai::nbdev&gt;=2.3.12
  23. # PyTorch &amp; Deep Learning
  24. # -------------------------------------------------------------------------
  25. - pytorch&gt;=2
  26. # NOTE: add pytorch-cuda if using a CUDA enabled GPU. You will need to
  27. # remove this if you are on Apple Silicon
  28. # - pytorch::pytorch-cuda
  29. - conda-forge::pytorch-lightning
  30. # Plotting
  31. # -------------------------------------------------------------------------
  32. - conda-forge::matplotlib
  33. - conda-forge::seaborn
  34. # Data Wrangling
  35. # -------------------------------------------------------------------------
  36. - conda-forge::scikit-learn
  37. - pandas&gt;=2
  38. - numpy
  39. - scipy
  40. # Pip / non-conda packages
  41. # -------------------------------------------------------------------------
  42. - pip
  43. - pip:
  44. # PyTorch &amp; Deep Learning
  45. # -----------------------------------------------------------------------
  46. - dgl

答案1

得分: 1

Current Solution

当前解决方案是文件 env_to_ini.py(查看 Files > env_to_ini.py 也许这个链接有效)。

注意:此解决方案使用 richtyper 创建了一个信息丰富的命令行界面(CLI),它将显示添加、更改、移除或未更改的依赖项。这应该会使与脚本一起工作更容易(特别是如果其中存在错误的话),因为它尚未经过广泛测试。

如何使用 env_to_ini.py

假设:

  • 项目根目录下有 env.ymlenv.mac.yml 文件。
  • 项目根目录下有 settings.ini 文件。
  • 项目根目录下有 env_to_ini.py 文件。

此脚本提供了这样一个功能,如果 env.yml(或 env.mac.yml)文件发生更改,您可以自动更新 current_project 包(在 settings.ini 中)的依赖关系,使其与之匹配。

  1. # 默认用法
  2. $ python env_to_ini.py
  3. # 显示未更改的包
  4. $ python env_to_ini.py --unchanged
  5. # 指定不同的环境文件
  6. $ python env_to_ini.py --unchanged --file=env.mac.yml

注意事项

这有点糟糕。您可以根据需要为每个项目进行修改。所谓的 "糟糕" 主要位于两个 TODO 下,我现在会解释。注意TODO 2 比 TODO 1 更重要。

TODO 1:排除

在提供的脚本中搜索以下内容:

  1. # TODO: 改进 1:使用要排除的包的列表。
  2. # 默认情况下,这将包括 python 和 pip。

根据原始问题,需要从 conda 环境文件 env.yml 中排除一些包。即 python。在第一个 TODO 下,通过 if / elif / else 语句目前实现了这一点,您可以更改脚本以接受包含要排除的包或包含这些包的附加文件的额外参数。

注意
如果您修改 env.yml 文件以在 dependencies 后面添加一个名为 ignore 的部分,不清楚它是否会影响您在 conda 中的使用。例如:

  1. dependencies:
  2. - python>=3.10
  3. # - ...
  4. ignore:
  5. - python
  6. - pip
  7. # ...

TODO 2:映射

在提供的脚本中搜索以下内容:

  1. # TODO: 改进 2:使用要重命名的包的映射。
  2. # 默认情况下,这将包括 pytorch --> torch。
  3. # 理想情况下,它会自动弄清楚。

根据原始问题,需要重命名一些包,因为它们在 conda 上的包名称与在 pypi 上的包名称不同。给出的示例是 pytorch,在 pypi 上列为 torch,在 conda 上列为 pytorch

在函数 requirements_to_ini 下,目前通过使用 if / elif / else 语句来实现这一点。您可以更改脚本以接受包含要重命名的包或包含这些包的附加文件的额外参数。

注意
如果您修改 env.yml 文件以在 dependencies 后面添加一个名为 rename 的部分,不清楚它是否会影响您在 conda 中的使用。例如:

  1. dependencies:
  2. - python>=3.10
  3. # - ...
  4. rename:
  5. - pytorch,torch
  6. # ...

注意
根据您的问题的期望结果,不清楚如何自动确定这一点。

Files

env_to_ini.py

  1. # env_to_ini.py
  2. import yaml
  3. import configparser
  4. from rich.console import Console
  5. from rich.table import Table
  6. import typer
  7. from typing import Optional, Tuple
  8. app = typer.Typer()
  9. console = Console()
  10. # NOTE: utility function to print colored text
  11. def cprint(style: str, text: str) -> None:
  12. console.print(f"[{style}]{text}[/{style}]")
  13. def has_channel(requirements_str: str) -> bool:
  14. return '::' in requirements_str
  15. def extract_channel(requirements_str: str) -> Tuple[Optional[str], str]:
  16. channel = None
  17. if has_channel(requirements_str):
  18. channel, requirements_str = requirements_str.split('::', 1)
  19. return channel, requirements_str
  20. def is_not_valid_package_char(s: str) -> bool:
  21. return not (s.isalnum() or s in ['-', '_', '.'])
  22. def split_str_at_first_non_alpha(s: str) -> Tuple[str, str]:
  23. idx = next((
  24. i for i, char in enumerate(s)
  25. if is_not_valid_package_char(char)
  26. ), len(s))
  27. return s[:idx], s[idx:]
  28. def split_package_version(s: str) -> Tuple[str, str]:
  29. # NOTE: alias for split_str_at_first_non_alpha
  30. return split_str_at_first_non_alpha(s)
  31. # NOTE: this parses requirements from the settings.ini file. Thus there is one line and each package is separated by a space.
  32. def parse_requirements(requirements_str):
  33. requirements = {}
  34. for req in requirements_str.split():
  35. package, version = split_package_version(req)
  36. requirements[package] = version
  37. return requirements
  38. # NOTE: this parse depdencies form the env.yml file.
  39. def extract_packages(dependencies):
  40. packages = {}
  41. for dep in dependencies:
  42. if isinstance(dep, str):
  43. channel, package_version = extract_channel(dep)
  44. package, version = split_package_version(package_version)
  45. # TODO: IMPROVEMENT 1: utilize a list of packages to exclude.
  46. # by default this would include python and pip.
  47. # NOTE: we do not need to add python to the requirements
  48. if package == 'python':
  49. continue
  50. # NOTE: likewise we do not need pip
  51. elif package == 'pip':
  52. continue
  53. packages[package] = version
  54. <details>
  55. <summary>英文:</summary>
  56. # Current Solution
  57. The current solution is the file `env_to_ini.py` (see `Files &gt; env_to_ini.py` [maybe this link works][script]).
  58. **NOTE** this solution uses [`rich`][rich] and [`typer`][typer] to create an informative command line interface (CLI) which will show you which dependencies were added, changed, removed, or unchanged. This should make working with the script easier (especially should there be bugs in it) as it has not been extensively tested.
  59. ## How to use `env_to_ini.py`
  60. Assumptions:
  61. - `env.yml` or `env.mac.yml` under project root
  62. - `settings.ini` under project root
  63. - `env_to_ini.py` under project root
  64. This script is provided so that if the `env.yml` (or `env.mac.yml`) file changes you can automatically update the dependencies of the `current_project` package (under `settings.ini`) to match.
  65. ```shell
  66. # default usage
  67. $ python env_to_ini.py
  68. # show packages that didnt change
  69. $ python env_to_ini.py --unchanged
  70. # specify a different environment file
  71. $ python env_to_ini.py --unchanged --file=env.mac.yml

Caveats

This is a bit hacky. You can modify it per project as needed. The so called "hackiness" is primarily located under the two TODOs, which I will now explain. Note that TODO 2 is more important than TODO 1.

TODO 1: exclusion

Search for the following in the provided script:

  1. # TODO: IMPROVEMENT 1: utilize a list of packages to exclude.
  2. # by default this would include python and pip.

Per the original question, some packages are to be excluded from the conda environment file env.yml. Namely, python. Under the first TODO, which achieves this currently via an if / elif / else statement, you could change the script to accept an additional argument containing package to exclude, or read in an additional file containing these.

NOTE:
It is unclear to me that if you were to modify your env.yml file to have a section after dependencies called ignore if it would mess up your usage with conda. e.g.

  1. dependencies:
  2. - python&gt;=3.10
  3. # - ...
  4. ignore:
  5. - python
  6. - pip
  7. # ...

TODO 2: mapping

Search for the following in the provided script:

  1. # TODO: IMPROVEMENT 2: utilize a map of packages to rename.
  2. # by default this would include pytorch --&gt; torch.
  3. # Ideally, this would figure it out automatically.

Per the original question, some packages need to be renamed because their package name on conda is different than it is on pypi. The example give is pytorch which is listed as torch for pypi and pytorch for conda.

Under the function requirements_to_ini this is currently achieved by using an if / elif / else statement. You could change the script to accept an additional argument containing package to rename, or read in an additional file containing these.

NOTE:
It is unclear to me that if you were to modify your env.yml file to have a section after dependencies called rename if it would mess up your usage with conda. e.g.

  1. dependencies:
  2. - python&gt;=3.10
  3. # - ...
  4. rename:
  5. - pytorch,torch
  6. # ...

NOTE:
It is unclear to me how you could determine this automatically per your question's desired outcome.

Files

env_to_ini.py

  1. # env_to_ini.py
  2. import yaml
  3. import configparser
  4. from rich.console import Console
  5. from rich.table import Table
  6. import typer
  7. from typing import Optional, Tuple
  8. app = typer.Typer()
  9. console = Console()
  10. # NOTE: utility function to print colored text
  11. def cprint(style:str, text:str) -&gt; None:
  12. console.print(f&quot;[{style}]{text}[/{style}]&quot;)
  13. def has_channel(requirements_str:str) -&gt; bool:
  14. return &#39;::&#39; in requirements_str
  15. def extract_channel(requirements_str:str) -&gt; Tuple[Optional[str], str]:
  16. channel = None
  17. if has_channel(requirements_str):
  18. channel, requirements_str = requirements_str.split(&#39;::&#39;, 1)
  19. return channel, requirements_str
  20. def is_not_valid_package_char(s:str) -&gt; bool:
  21. return not (s.isalnum() or s in [&#39;-&#39;, &#39;_&#39;, &#39;.&#39;])
  22. def split_str_at_first_non_alpha(s:str) -&gt; Tuple[str, str]:
  23. idx = next((
  24. i for i, char in enumerate(s)
  25. if is_not_valid_package_char(char)
  26. ), len(s))
  27. return s[:idx], s[idx:]
  28. def split_package_version(s:str) -&gt; Tuple[str, str]:
  29. # NOTE: alias for split_str_at_first_non_alpha
  30. return split_str_at_first_non_alpha(s)
  31. # NOTE: this parses requirements from the settings.ini file. Thus there is one line and each package is separated by a space.
  32. def parse_requirements(requirements_str):
  33. requirements = {}
  34. for req in requirements_str.split():
  35. package, version = split_package_version(req)
  36. requirements[package] = version
  37. return requirements
  38. # NOTE: this parse depdencies form the env.yml file.
  39. def extract_packages(dependencies):
  40. packages = {}
  41. for dep in dependencies:
  42. if isinstance(dep, str):
  43. channel, package_version = extract_channel(dep)
  44. package, version = split_package_version(package_version)
  45. # TODO: IMPROVEMENT 1: utilize a list of packages to exclude.
  46. # by default this would include python and pip.
  47. # NOTE: we do not need to add python to the requirements
  48. if package == &#39;python&#39;:
  49. continue
  50. # NOTE: likewise we do not need pip
  51. elif package == &#39;pip&#39;:
  52. continue
  53. packages[package] = version
  54. elif isinstance(dep, dict):
  55. for key, values in dep.items():
  56. if key == &#39;pip&#39;:
  57. for pip_dep in values:
  58. package, version = split_package_version(pip_dep)
  59. packages[package] = version
  60. return packages
  61. # NOTE: check if the depdencies in the env.yml file vary from the ones in the settings.ini file.
  62. def compare_requirements(old, new):
  63. added = {k: v for k, v in new.items() if k not in old}
  64. removed = {k: v for k, v in old.items() if k not in new}
  65. changed = {k: (old[k], new[k]) for k in old if k in new and old[k] != new[k]}
  66. remained = {k: (old[k], new[k]) for k in old if k in new and old[k] == new[k]}
  67. return added, removed, changed, remained
  68. # NOTE: I like pretty terminals
  69. def print_changes(added, removed, changed, remained):
  70. table = Table(title=&quot;Changes&quot;)
  71. table.add_column(&quot;Package&quot;, style=&quot;cyan&quot;)
  72. table.add_column(&quot;Old Version&quot;, style=&quot;magenta&quot;)
  73. table.add_column(&quot;New Version&quot;, style=&quot;green&quot;)
  74. table.add_column(&quot;Status&quot;, style=&quot;yellow&quot;)
  75. for package, version in added.items():
  76. table.add_row(f&#39;:package: {package}&#39;, &quot;&quot;, version, &quot;Added&quot;)
  77. for package, version in removed.items():
  78. table.add_row(f&#39;:package: {package}&#39;, version, &quot;&quot;, &quot;Removed&quot;)
  79. for package, versions in changed.items():
  80. table.add_row(f&#39;:package: {package}&#39;, versions[0], versions[1], &quot;Changed&quot;)
  81. for package, versions in remained.items():
  82. table.add_row(f&#39;:package: {package}&#39;, versions[0], versions[1], &quot;Unchanged&quot;)
  83. console.print(table)
  84. def requirements_to_ini(requirments:dict) -&gt; str:
  85. ini = &#39;&#39;
  86. for package, version in requirments.items():
  87. # TODO: IMPROVEMENT 2: utilize a map of packages to rename.
  88. # by default this would include pytorch --&gt; torch.
  89. # Ideally, this would figure it out automatically.
  90. # NOTE: this is a hack to make the env.yml file compatible with the settings.ini file
  91. # since the env.yml file uses pytorch and the settings.ini file uses torch.
  92. # Add more elif statements if you need to change other package names.
  93. if package == &#39;pytorch&#39;:
  94. package = &#39;torch&#39;
  95. if version:
  96. ini += f&quot;{package}{version} &quot;
  97. else:
  98. ini += f&quot;{package} &quot;
  99. return ini
  100. @app.command()
  101. def update_requirements(
  102. file: Optional[str] = typer.Option(
  103. &#39;env.mac.yml&#39;,
  104. help=&quot;YAML file to extract the new requirements from.&quot;,
  105. ),
  106. unchanged: Optional[bool] = typer.Option(
  107. False,
  108. help=&quot;Whether to print all packages, including the ones whose versions haven&#39;t changed.&quot;,
  109. ),
  110. ):
  111. # NOTE: notice that file is `env.mac.yml` and not `env.yml`. Now with Apple Silicon I have
  112. # one env file for more common CUDA versions and one for Apple Silicon.
  113. cprint(&quot;bold cyan&quot;, f&quot;Loading environment yaml file {file}...&quot;)
  114. with open(file, &#39;r&#39;) as f:
  115. env = yaml.safe_load(f)
  116. # NOTE: read in the current dependencies from the conda env.yml file
  117. cprint(&quot;bold cyan&quot;, &quot;Extracting packages and their versions...&quot;)
  118. new_requirements = extract_packages(env[&#39;dependencies&#39;])
  119. # NOTE: read in the previous requirements from the settings.ini file
  120. cprint(&quot;bold cyan&quot;, &quot;Loading settings.ini file...&quot;)
  121. config = configparser.ConfigParser()
  122. config.read(&#39;settings.ini&#39;)
  123. cprint(&quot;bold cyan&quot;, &quot;Comparing the old and new requirements...&quot;)
  124. old_requirements = parse_requirements(config[&#39;DEFAULT&#39;][&#39;requirements&#39;])
  125. # NOTE: check for changes
  126. added, removed, changed, remained = compare_requirements(old_requirements, new_requirements)
  127. # If --unchanged option is given, print unchanged packages as well
  128. if unchanged:
  129. print_changes(added, removed, changed, remained)
  130. else:
  131. print_changes(added, removed, changed, {})
  132. # NOTE: update the requirements in the settings.ini file
  133. cprint(&quot;bold cyan&quot;, &quot;Updating the requirements...&quot;)
  134. config[&#39;DEFAULT&#39;][&#39;requirements&#39;] = requirements_to_ini(new_requirements)
  135. cprint(&quot;bold cyan&quot;, &quot;Saving the updated settings.ini file...&quot;)
  136. with open(&#39;settings.ini&#39;, &#39;w&#39;) as f:
  137. config.write(f)
  138. cprint(&quot;bold green&quot;, &quot;Successfully updated the requirements in settings.ini!&quot;)
  139. if __name__ == &quot;__main__&quot;:
  140. app()

Update env2ini

Now it is a CLI on pypi, conda, github, and "docs"

huangapple
  • 本文由 发表于 2023年5月29日 00:22:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76352457.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定