wetlands 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wetlands-0.1.2/.github/workflows/ci.yml +89 -0
- wetlands-0.1.2/.gitignore +14 -0
- wetlands-0.1.2/PKG-INFO +132 -0
- wetlands-0.1.2/README.md +114 -0
- wetlands-0.1.2/Wetland.png +0 -0
- wetlands-0.1.2/docs/Wetland.svg +6 -0
- wetlands-0.1.2/docs/WetlandInverted.svg +6 -0
- wetlands-0.1.2/docs/advanced_example.md +194 -0
- wetlands-0.1.2/docs/getting_started.md +300 -0
- wetlands-0.1.2/docs/how_it_works.md +50 -0
- wetlands-0.1.2/docs/index.md +82 -0
- wetlands-0.1.2/examples/advanced_example.py +58 -0
- wetlands-0.1.2/examples/advanced_example_module.py +29 -0
- wetlands-0.1.2/examples/example_module.py +56 -0
- wetlands-0.1.2/examples/getting_started.py +37 -0
- wetlands-0.1.2/examples/minimal_example.py +21 -0
- wetlands-0.1.2/examples/minimal_module.py +4 -0
- wetlands-0.1.2/mkdocs.yml +67 -0
- wetlands-0.1.2/pyproject.toml +44 -0
- wetlands-0.1.2/scripts/gen_ref_pages.py +43 -0
- wetlands-0.1.2/src/wetlands/_internal/command_executor.py +176 -0
- wetlands-0.1.2/src/wetlands/_internal/command_generator.py +165 -0
- wetlands-0.1.2/src/wetlands/_internal/dependency_manager.py +164 -0
- wetlands-0.1.2/src/wetlands/_internal/exceptions.py +15 -0
- wetlands-0.1.2/src/wetlands/_internal/module_executor.py +171 -0
- wetlands-0.1.2/src/wetlands/_internal/settings_manager.py +84 -0
- wetlands-0.1.2/src/wetlands/environment.py +105 -0
- wetlands-0.1.2/src/wetlands/environment_manager.py +251 -0
- wetlands-0.1.2/src/wetlands/external_environment.py +138 -0
- wetlands-0.1.2/src/wetlands/internal_environment.py +40 -0
- wetlands-0.1.2/src/wetlands/logger.py +46 -0
- wetlands-0.1.2/tests/conftest.py +21 -0
- wetlands-0.1.2/tests/test_command_executor.py +42 -0
- wetlands-0.1.2/tests/test_command_generator.py +73 -0
- wetlands-0.1.2/tests/test_dependency_manager.py +85 -0
- wetlands-0.1.2/tests/test_environment.py +58 -0
- wetlands-0.1.2/tests/test_environment_manager.py +539 -0
- wetlands-0.1.2/tests/test_external_environment.py +85 -0
- wetlands-0.1.2/tests/test_internal_environment.py +41 -0
- wetlands-0.1.2/tests/test_module_executor.py +50 -0
- wetlands-0.1.2/tests/test_settings_manager.py +88 -0
- wetlands-0.1.2/tests/test_wetlands.py +276 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- master
|
|
7
|
+
- main
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
|
|
14
|
+
lint:
|
|
15
|
+
if: "!contains(github.event.head_commit.message, '[skip tests]')" # skip the job if the commit message contains "[skip tests]"
|
|
16
|
+
name: Lint
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: 3.x
|
|
23
|
+
- name: Install uv
|
|
24
|
+
run: pip install uv
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: uv pip install ".[docs]" --system
|
|
27
|
+
- name: Run lint checks
|
|
28
|
+
run: uv run ruff check
|
|
29
|
+
|
|
30
|
+
test:
|
|
31
|
+
if: "!contains(github.event.head_commit.message, '[skip tests]')" # skip the job if the commit message contains "[skip tests]"
|
|
32
|
+
name: Test (Python ${{ matrix.python-version }})
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
strategy:
|
|
35
|
+
matrix:
|
|
36
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
- uses: actions/setup-python@v5
|
|
40
|
+
with:
|
|
41
|
+
python-version: ${{ matrix.python-version }}
|
|
42
|
+
- name: Install uv
|
|
43
|
+
run: pip install uv
|
|
44
|
+
- name: Install dependencies
|
|
45
|
+
run: uv pip install ".[docs]" --system
|
|
46
|
+
- name: Run tests
|
|
47
|
+
run: uv run pytest tests
|
|
48
|
+
|
|
49
|
+
deploy:
|
|
50
|
+
if: always() # if: always() ensures the deploy job runs even if lint and test were skipped — but still requires them to finish successfully if they did run.
|
|
51
|
+
name: Deploy
|
|
52
|
+
needs: [lint, test]
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@v4
|
|
56
|
+
|
|
57
|
+
- name: Configure Git Credentials
|
|
58
|
+
run: |
|
|
59
|
+
git config user.name github-actions[bot]
|
|
60
|
+
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
|
61
|
+
|
|
62
|
+
- uses: actions/setup-python@v5
|
|
63
|
+
with:
|
|
64
|
+
python-version: 3.x
|
|
65
|
+
|
|
66
|
+
- name: Set up cache
|
|
67
|
+
run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
|
68
|
+
|
|
69
|
+
- uses: actions/cache@v4
|
|
70
|
+
with:
|
|
71
|
+
key: mkdocs-material-${{ env.cache_id }}
|
|
72
|
+
path: .cache
|
|
73
|
+
restore-keys: |
|
|
74
|
+
mkdocs-material-
|
|
75
|
+
|
|
76
|
+
- name: Install uv
|
|
77
|
+
run: pip install uv
|
|
78
|
+
|
|
79
|
+
- name: Install docs dependencies
|
|
80
|
+
run: uv pip install ".[docs]" --system
|
|
81
|
+
|
|
82
|
+
- name: Get gh-pages branch
|
|
83
|
+
run: git fetch origin gh-pages --depth=1
|
|
84
|
+
|
|
85
|
+
- name: Set version default
|
|
86
|
+
run: mike set-default --push latest
|
|
87
|
+
|
|
88
|
+
- name: Deploy version with mike (update gh-pages branch and push it)
|
|
89
|
+
run: mike deploy --push --update-aliases 1.0 latest
|
wetlands-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wetlands
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Wetlands is a lightweight library to create conda environment and execute code inside them.
|
|
5
|
+
Author-email: Arthur Masson <arthur.masson@inria.fr>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: psutil>=6.1.0
|
|
9
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
10
|
+
Requires-Dist: typing-extensions>=4.0; python_version < '3.11'
|
|
11
|
+
Provides-Extra: docs
|
|
12
|
+
Requires-Dist: mike>=2.1.3; extra == 'docs'
|
|
13
|
+
Requires-Dist: mkdocs-gen-files>=0.5.0; extra == 'docs'
|
|
14
|
+
Requires-Dist: mkdocs-literate-nav>=0.6.2; extra == 'docs'
|
|
15
|
+
Requires-Dist: mkdocs-material>=9.6.11; extra == 'docs'
|
|
16
|
+
Requires-Dist: mkdocstrings[python]>=0.29.1; extra == 'docs'
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
------
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
# Wetlands - Conda Environment Manager
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
**Wetlands** is a lightweight Python library for managing **Conda** environments.
|
|
30
|
+
|
|
31
|
+
**Wetlands** can create Conda environments on demand, install dependencies, and execute arbitrary code within them. This makes it easy to build *plugin systems* or integrate external modules into an application without dependency conflicts, as each environment remains isolated.
|
|
32
|
+
|
|
33
|
+
The name ***Wetlands*** comes from the tropical *environments* where Anacondas thrive.
|
|
34
|
+
|
|
35
|
+
## ✨ Features
|
|
36
|
+
|
|
37
|
+
- **Automatic Environment Management**: Create and configure environments on demand.
|
|
38
|
+
- **Dependency Isolation**: Install dependencies without conflicts.
|
|
39
|
+
- **Embedded Execution**: Run Python functions inside isolated environments.
|
|
40
|
+
- **Micromamba**: Wetlands uses a self-contained `micromamba` for fast and lightweight Conda environment handling.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 📦 Installation
|
|
45
|
+
|
|
46
|
+
To install **Wetlands**, simply use `pip`:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
pip install wetlands
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 🚀 Usage Example
|
|
53
|
+
|
|
54
|
+
If the user doesn't have micromamba installed, Wetlands will download and set it up automatically.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
|
|
58
|
+
from wetlands.environment_manager import EnvironmentManager
|
|
59
|
+
|
|
60
|
+
# Initialize the environment manager
|
|
61
|
+
# Wetlands will use the existing Micromamba installation at the specified path (e.g., "micromamba/") if available;
|
|
62
|
+
# otherwise it will automatically download and install Micromamba in a self-contained manner.
|
|
63
|
+
environmentManager = EnvironmentManager("micromamba/")
|
|
64
|
+
|
|
65
|
+
# Create and launch an isolated Conda environment named "numpy"
|
|
66
|
+
env = environmentManager.create("numpy", {"pip":["numpy==2.2.4"]})
|
|
67
|
+
env.launch()
|
|
68
|
+
|
|
69
|
+
# Import example_module in the environment, see example_module.py below
|
|
70
|
+
minimal_module = env.importModule("minimal_module.py")
|
|
71
|
+
# example_module is a proxy to example_module.py in the environment
|
|
72
|
+
array = [1,2,3]
|
|
73
|
+
result = minimal_module.sum(array)
|
|
74
|
+
|
|
75
|
+
print(f"Sum of {array} is {result}.")
|
|
76
|
+
|
|
77
|
+
# Clean up and exit the environment
|
|
78
|
+
env.exit()
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
with `example_module.py` as follow:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
def sum(x):
|
|
86
|
+
import numpy as np
|
|
87
|
+
return int(np.sum(x))
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
See the `examples/` folder for more detailed examples.
|
|
91
|
+
|
|
92
|
+
## 🔗 Related Projects
|
|
93
|
+
|
|
94
|
+
- [Conda](https://anaconda.org/)
|
|
95
|
+
- [Micromamba](https://mamba.readthedocs.io/en/latest/user_guide/micromamba.html)
|
|
96
|
+
|
|
97
|
+
## 🤖 Development
|
|
98
|
+
|
|
99
|
+
Use [uv](https://docs.astral.sh/uv/) to easily manage the project.
|
|
100
|
+
|
|
101
|
+
### Check & Format
|
|
102
|
+
|
|
103
|
+
Check for code errors with `uv run ruff check` and format the code with `uv run ruff format`.
|
|
104
|
+
|
|
105
|
+
### Tests
|
|
106
|
+
|
|
107
|
+
Test wetlands with `uv` and `ipdb`: `uv run pytest --pdb --pdbcls=IPython.terminal.debugger:TerminalPdb tests`
|
|
108
|
+
|
|
109
|
+
### Generate documentation
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
The Wetlands documentation is generated with [`mkdocs-material`](https://squidfunk.github.io/mkdocs-material/), [`mkdocstrings`](https://mkdocstrings.github.io/), [`mike`](https://github.com/jimporter/mike) and others.
|
|
113
|
+
|
|
114
|
+
Install the doc tools with `uv pip install ".[docs]"`.
|
|
115
|
+
|
|
116
|
+
MkDocs includes a live preview server, so you can preview your changes as you write your documentation. The server will automatically rebuild the site upon saving. Start it with: `uv run mkdocs serve`.
|
|
117
|
+
|
|
118
|
+
[`mike`](https://github.com/jimporter/mike) is used to generate multiple versions of the docs. To create a new version, use `mike deploy [version]` (do not forget to update `.github/workflows/ci.yml`).
|
|
119
|
+
|
|
120
|
+
The doc is automatically generated by [Github Actions](https://squidfunk.github.io/mkdocs-material/publishing-your-site/#with-github-actions-material-for-mkdocs) (see `.github/workflows/ci.yml`).
|
|
121
|
+
|
|
122
|
+
The script `scripts/gen_ref_pages.py` generates the API reference automatically (see [mkdocstrings recipes](https://mkdocstrings.github.io/recipes/)).
|
|
123
|
+
|
|
124
|
+
## 📋 Todo
|
|
125
|
+
|
|
126
|
+
- Handle general [dependency specifiers](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#dependency-specifiers) (handle version specifiers like '>=', '~=' etc.).
|
|
127
|
+
|
|
128
|
+
## 📜 License
|
|
129
|
+
|
|
130
|
+
This project was made at Inria in Rennes (Centre Inria de l'Université de Rennes) and is licensed under the MIT License.
|
|
131
|
+
|
|
132
|
+
The logo Wetland was made by [Dan Hetteix](https://thenounproject.com/creator/DHETTEIX/) from Noun Project (CC BY 3.0).
|
wetlands-0.1.2/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
3
|
+
------
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
# Wetlands - Conda Environment Manager
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
**Wetlands** is a lightweight Python library for managing **Conda** environments.
|
|
12
|
+
|
|
13
|
+
**Wetlands** can create Conda environments on demand, install dependencies, and execute arbitrary code within them. This makes it easy to build *plugin systems* or integrate external modules into an application without dependency conflicts, as each environment remains isolated.
|
|
14
|
+
|
|
15
|
+
The name ***Wetlands*** comes from the tropical *environments* where Anacondas thrive.
|
|
16
|
+
|
|
17
|
+
## ✨ Features
|
|
18
|
+
|
|
19
|
+
- **Automatic Environment Management**: Create and configure environments on demand.
|
|
20
|
+
- **Dependency Isolation**: Install dependencies without conflicts.
|
|
21
|
+
- **Embedded Execution**: Run Python functions inside isolated environments.
|
|
22
|
+
- **Micromamba**: Wetlands uses a self-contained `micromamba` for fast and lightweight Conda environment handling.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 📦 Installation
|
|
27
|
+
|
|
28
|
+
To install **Wetlands**, simply use `pip`:
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
pip install wetlands
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 🚀 Usage Example
|
|
35
|
+
|
|
36
|
+
If the user doesn't have micromamba installed, Wetlands will download and set it up automatically.
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
|
|
40
|
+
from wetlands.environment_manager import EnvironmentManager
|
|
41
|
+
|
|
42
|
+
# Initialize the environment manager
|
|
43
|
+
# Wetlands will use the existing Micromamba installation at the specified path (e.g., "micromamba/") if available;
|
|
44
|
+
# otherwise it will automatically download and install Micromamba in a self-contained manner.
|
|
45
|
+
environmentManager = EnvironmentManager("micromamba/")
|
|
46
|
+
|
|
47
|
+
# Create and launch an isolated Conda environment named "numpy"
|
|
48
|
+
env = environmentManager.create("numpy", {"pip":["numpy==2.2.4"]})
|
|
49
|
+
env.launch()
|
|
50
|
+
|
|
51
|
+
# Import example_module in the environment, see example_module.py below
|
|
52
|
+
minimal_module = env.importModule("minimal_module.py")
|
|
53
|
+
# example_module is a proxy to example_module.py in the environment
|
|
54
|
+
array = [1,2,3]
|
|
55
|
+
result = minimal_module.sum(array)
|
|
56
|
+
|
|
57
|
+
print(f"Sum of {array} is {result}.")
|
|
58
|
+
|
|
59
|
+
# Clean up and exit the environment
|
|
60
|
+
env.exit()
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
with `example_module.py` as follow:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
def sum(x):
|
|
68
|
+
import numpy as np
|
|
69
|
+
return int(np.sum(x))
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
See the `examples/` folder for more detailed examples.
|
|
73
|
+
|
|
74
|
+
## 🔗 Related Projects
|
|
75
|
+
|
|
76
|
+
- [Conda](https://anaconda.org/)
|
|
77
|
+
- [Micromamba](https://mamba.readthedocs.io/en/latest/user_guide/micromamba.html)
|
|
78
|
+
|
|
79
|
+
## 🤖 Development
|
|
80
|
+
|
|
81
|
+
Use [uv](https://docs.astral.sh/uv/) to easily manage the project.
|
|
82
|
+
|
|
83
|
+
### Check & Format
|
|
84
|
+
|
|
85
|
+
Check for code errors with `uv run ruff check` and format the code with `uv run ruff format`.
|
|
86
|
+
|
|
87
|
+
### Tests
|
|
88
|
+
|
|
89
|
+
Test wetlands with `uv` and `ipdb`: `uv run pytest --pdb --pdbcls=IPython.terminal.debugger:TerminalPdb tests`
|
|
90
|
+
|
|
91
|
+
### Generate documentation
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
The Wetlands documentation is generated with [`mkdocs-material`](https://squidfunk.github.io/mkdocs-material/), [`mkdocstrings`](https://mkdocstrings.github.io/), [`mike`](https://github.com/jimporter/mike) and others.
|
|
95
|
+
|
|
96
|
+
Install the doc tools with `uv pip install ".[docs]"`.
|
|
97
|
+
|
|
98
|
+
MkDocs includes a live preview server, so you can preview your changes as you write your documentation. The server will automatically rebuild the site upon saving. Start it with: `uv run mkdocs serve`.
|
|
99
|
+
|
|
100
|
+
[`mike`](https://github.com/jimporter/mike) is used to generate multiple versions of the docs. To create a new version, use `mike deploy [version]` (do not forget to update `.github/workflows/ci.yml`).
|
|
101
|
+
|
|
102
|
+
The doc is automatically generated by [Github Actions](https://squidfunk.github.io/mkdocs-material/publishing-your-site/#with-github-actions-material-for-mkdocs) (see `.github/workflows/ci.yml`).
|
|
103
|
+
|
|
104
|
+
The script `scripts/gen_ref_pages.py` generates the API reference automatically (see [mkdocstrings recipes](https://mkdocstrings.github.io/recipes/)).
|
|
105
|
+
|
|
106
|
+
## 📋 Todo
|
|
107
|
+
|
|
108
|
+
- Handle general [dependency specifiers](https://packaging.python.org/en/latest/specifications/dependency-specifiers/#dependency-specifiers) (handle version specifiers like '>=', '~=' etc.).
|
|
109
|
+
|
|
110
|
+
## 📜 License
|
|
111
|
+
|
|
112
|
+
This project was made at Inria in Rennes (Centre Inria de l'Université de Rennes) and is licensed under the MIT License.
|
|
113
|
+
|
|
114
|
+
The logo Wetland was made by [Dan Hetteix](https://thenounproject.com/creator/DHETTEIX/) from Noun Project (CC BY 3.0).
|
|
Binary file
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg width="91" height="91" viewBox="0 0 91 91" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M80.0903 71.929C78.7503 70.95 76.9604 69.639 73.7404 69.499C73.5704 69.489 73.3803 69.489 73.1703 69.489C72.9903 69.489 72.7904 69.489 72.6104 69.499C69.4104 69.639 67.5803 70.949 66.2503 71.929C65.2303 72.65 64.6103 73.089 63.3903 73.209C63.2103 73.219 63.0213 73.219 62.8213 73.219C62.6313 73.219 62.4304 73.219 62.2714 73.209C61.0504 73.089 60.4204 72.649 59.4114 71.929C58.0614 70.95 56.2514 69.639 53.0414 69.499C52.8524 69.489 52.6713 69.489 52.4613 69.489C52.2723 69.489 52.0914 69.489 51.9114 69.499C48.7114 69.639 46.8904 70.949 45.5414 71.929C44.5314 72.65 43.9014 73.089 42.6814 73.209C42.5214 73.219 42.3214 73.219 42.1314 73.219C41.9214 73.219 41.7414 73.219 41.5614 73.209C40.3414 73.089 39.7214 72.649 38.7014 71.929C37.3614 70.95 35.5414 69.639 32.3414 69.499C32.1614 69.489 31.9514 69.489 31.7714 69.489C31.5814 69.489 31.3814 69.489 31.2014 69.499C28.0014 69.639 26.1914 70.949 24.8414 71.929C23.8414 72.65 23.2013 73.089 21.9813 73.209C21.8013 73.219 21.6214 73.219 21.4114 73.219C21.2214 73.219 21.0213 73.219 20.8613 73.209C19.6413 73.089 19.0114 72.649 18.0014 71.929C16.6514 70.95 14.8513 69.639 11.6413 69.499C11.5013 69.489 11.3614 69.489 7.32135 69.489C9.44135 72.939 12.0213 76.069 14.9713 78.809C15.6013 78.949 16.2813 79.038 17.0313 79.069C17.2413 79.099 17.4514 79.099 17.6614 79.099C17.8714 79.099 18.1013 79.099 18.3013 79.069C21.8913 78.919 23.9313 77.459 25.4513 76.369C26.5813 75.559 27.2714 75.049 28.6314 74.929C28.8414 74.919 29.0613 74.919 29.2813 74.919C29.4913 74.919 29.7213 74.919 29.9213 74.929C31.2813 75.049 31.9713 75.56 33.1013 76.369C34.6213 77.459 36.6614 78.919 40.2514 79.069C40.4514 79.099 40.6613 79.099 40.8913 79.099C41.1013 79.099 41.3013 79.099 41.5113 79.069C45.1113 78.919 47.1404 77.459 48.6614 76.369C49.7914 75.559 50.5014 75.049 51.8614 74.929C52.0414 74.919 52.2713 74.919 52.4813 74.919C52.7203 74.919 52.9203 74.919 53.1213 74.929C54.4913 75.049 55.2013 75.56 56.3313 76.369C57.8523 77.459 59.8814 78.919 63.4813 79.069C63.6803 79.099 63.8804 79.099 64.1014 79.099C64.3214 79.099 64.5404 79.099 64.7414 79.069C68.3314 78.919 70.3714 77.459 71.8714 76.369C73.0114 75.559 73.7014 75.049 75.0714 74.929C75.2714 74.919 75.4703 74.919 75.7113 74.919C75.9523 74.919 76.1514 74.919 76.3524 74.929C77.5014 75.03 78.1714 75.409 79.0414 76.009C79.9514 74.999 80.8123 73.949 81.6313 72.869C81.1303 72.649 80.6803 72.339 80.0903 71.929Z" fill="black"/>
|
|
3
|
+
<path d="M64.1004 85.829C60.1204 85.829 57.9294 84.269 56.3294 83.109C55.0394 82.189 54.3094 81.649 52.4794 81.649C50.6894 81.649 49.9494 82.189 48.6594 83.109C47.0494 84.258 44.8694 85.829 40.8894 85.829C36.8994 85.829 34.6994 84.269 33.0994 83.109C31.7994 82.189 31.0894 81.649 29.2694 81.649C27.4594 81.649 26.7394 82.189 25.4494 83.109C24.7394 83.619 23.9194 84.209 22.8694 84.699C29.5404 88.619 37.3204 90.869 45.6204 90.869C53.3004 90.869 60.5304 88.939 66.8604 85.549C66.0504 85.729 65.1404 85.829 64.1004 85.829Z" fill="black"/>
|
|
4
|
+
<path d="M36.8504 59.179C37.0104 59.749 37.1504 61.629 37.2604 63.48H35.0604C35.0204 61.9 35.0304 60.33 35.2404 59.719C35.7104 58.299 35.6404 56.959 35.5004 56.209C35.3704 55.469 36.1804 49.469 36.5204 48.659C36.8504 47.859 36.8504 47.859 36.8504 47.859L37.1204 49.199C37.1204 49.199 37.2604 54.459 36.9904 55.399C36.7204 56.349 36.5204 57.969 36.8504 59.179Z" fill="black"/>
|
|
5
|
+
<path d="M90.6204 45.869C90.6204 53.449 88.7404 60.589 85.4304 66.859L85.4204 66.869C84.9204 67.089 84.3604 67.209 83.5404 67.209C81.9204 67.209 81.2804 66.739 80.1014 65.909C78.6914 64.869 76.7504 63.479 73.1804 63.479C69.6204 63.479 67.6604 64.869 66.2504 65.909C65.0804 66.739 64.4504 67.209 62.8204 67.209C61.2104 67.209 60.5704 66.739 59.4104 65.909C57.9804 64.869 56.0304 63.479 52.4604 63.479H51.3904C51.4514 63.099 51.5104 62.708 51.5504 62.298C51.6304 61.868 51.7204 61.418 51.8004 60.968C51.8904 60.538 52.0214 60.138 52.1304 59.718C52.2104 59.519 52.2904 59.319 52.3704 59.128C52.4604 58.908 52.5404 58.698 52.6304 58.488C52.8114 58.078 52.9204 57.618 53.0904 57.288C53.2714 56.948 53.4014 56.599 53.5504 56.278C53.6704 55.958 53.7904 55.648 53.9304 55.398C54.0504 55.127 54.1704 54.898 54.2504 54.688C54.4104 54.348 54.5104 54.118 54.5304 54.068C54.5094 54.078 54.4604 54.108 54.3804 54.148C54.2714 54.198 54.1104 54.278 53.9304 54.408C53.5804 54.678 53.0404 55.028 52.5314 55.608C52.2814 55.898 51.9704 56.198 51.6914 56.558C51.4014 56.908 51.2014 57.268 50.9614 57.667C50.8414 57.857 50.7214 58.058 50.6024 58.257C50.5414 58.358 50.4824 58.457 50.4314 58.568L50.3414 58.717L50.3014 58.797L50.2814 58.837L50.2414 58.917L50.2314 58.947C50.0504 59.426 49.8404 59.926 49.6914 60.426C49.5614 60.926 49.4414 61.426 49.3404 61.916C49.2504 62.406 49.1804 62.886 49.1204 63.356C49.1204 63.396 49.1104 63.436 49.1104 63.476H47.5004C47.5614 62.875 47.6304 62.246 47.7004 61.596C47.7904 60.976 47.8804 60.335 47.9704 59.696C48.0904 59.066 48.2104 58.416 48.3304 57.776C48.4904 57.156 48.7004 56.566 48.8604 55.956C49.0704 55.416 49.3504 54.727 49.5894 54.176C49.8604 53.636 50.1004 53.116 50.3694 52.616C50.6384 52.116 50.8494 51.616 51.0994 51.176C51.5484 50.267 51.9284 49.496 52.1894 48.966C52.4494 48.406 52.5994 48.076 52.5994 48.076V48.069C52.5994 48.069 52.2584 48.218 51.7394 48.569C51.2094 48.919 50.5294 49.499 49.8094 50.289C49.4494 50.679 49.0994 51.149 48.7394 51.639C48.3794 52.139 48.0794 52.73 47.7294 53.32C47.4094 53.92 47.1494 54.45 46.8194 55.13C46.5694 55.811 46.3094 56.51 46.0994 57.21C45.9494 57.91 45.7594 58.601 45.6294 59.29C45.5194 59.98 45.4194 60.66 45.3294 61.311C45.2494 62.071 45.1794 62.8 45.1294 63.481H43.5594C43.6204 62.869 43.6504 62.199 43.7304 61.509C43.8204 60.719 43.9104 59.879 44.0004 59.02C44.1204 58.169 44.2504 57.29 44.3804 56.389C44.5504 55.52 44.7304 54.619 44.9104 53.73C45.1304 52.87 45.4104 52.03 45.6504 51.179C46.0004 50.35 46.3504 49.53 46.6914 48.73C47.0614 47.98 47.4014 47.24 47.7604 46.54C48.1404 45.85 48.4604 45.17 48.8114 44.57C49.4814 43.36 50.0814 42.35 50.5104 41.65C50.9414 40.94 51.2004 40.54 51.2004 40.54C51.2004 40.54 50.7904 40.79 50.1504 41.33C49.5094 41.88 48.6294 42.69 47.7104 43.81C47.2204 44.34 46.7804 44.99 46.2904 45.66C45.8004 46.32 45.3904 47.12 44.9004 47.9C44.4704 48.71 44.1304 49.48 43.6904 50.389C43.3804 51.31 43.0104 52.24 42.7404 53.179C42.5304 54.119 42.3004 55.07 42.1104 55.999C41.9704 56.939 41.8404 57.859 41.7104 58.749C41.5204 60.479 41.3904 62.089 41.3404 63.479H38.4004C38.3304 61.609 38.3104 59.718 38.4704 59.249C38.8104 58.239 38.6704 56.959 38.3404 56.009C38.0004 55.07 38.1304 52.709 38.2704 50.209C38.4004 47.719 39.8204 42.389 39.8204 42.389C39.8204 42.389 44.0704 36.999 46.1604 35.789C48.2504 34.569 53.9104 29.919 52.2904 23.379C50.6704 16.839 47.8404 17.719 46.2904 17.249C44.7404 16.779 44.8704 14.079 46.2904 13.879C47.7104 13.679 51.6804 13.409 51.6804 13.409C51.6804 13.409 52.9604 14.149 56.9414 14.619C60.9204 15.089 62.3314 14.759 62.3314 14.759C62.3314 14.759 59.6414 13.069 56.8114 12.399C53.9704 11.719 51.4114 10.639 51.4114 10.639C51.4114 10.639 48.2414 8.14903 45.6814 9.15903C43.1214 10.169 40.9714 12.059 41.5014 15.559C42.0414 19.069 44.0714 21.019 46.0914 21.359C48.1114 21.699 49.1214 22.579 48.7204 25.339C48.3804 27.669 46.7604 28.169 46.0914 27.899C45.4114 27.629 43.1914 24.999 40.9014 24.929C38.6014 24.869 36.7214 25.539 31.6014 31.269C26.4714 36.999 23.7114 41.859 22.2214 45.019C20.7414 48.189 18.9914 54.389 18.9214 56.409C18.8514 58.439 19.8714 58.099 20.6714 56.888C21.4814 55.668 23.6414 52.239 25.2614 51.218C26.8814 50.208 34.8314 45.698 34.8314 45.698C34.8314 45.698 35.5014 47.177 35.3014 49.128C35.1014 51.088 34.6314 54.328 34.1614 55.538C33.6814 56.748 33.5514 58.508 33.8214 60.188C33.9314 60.848 34.0014 62.137 34.0614 63.478H31.2614C31.2014 63.108 31.1414 62.738 31.0814 62.358C30.9914 61.898 30.8614 61.427 30.7514 60.967C30.6114 60.497 30.4314 60.018 30.2514 59.558L30.2014 59.448L30.1814 59.418L30.1414 59.338L30.0614 59.198L29.9014 58.918C29.7914 58.729 29.6914 58.538 29.5814 58.358C29.3614 57.988 29.2114 57.637 28.9414 57.297C28.6814 56.967 28.4214 56.657 28.1614 56.398C27.6514 55.858 27.1214 55.547 26.7514 55.337C26.3714 55.137 26.1214 55.077 26.1214 55.077H26.1114C26.1114 55.077 26.1814 55.337 26.3014 55.737C26.3614 55.937 26.4414 56.167 26.5414 56.426C26.6514 56.676 26.7514 56.956 26.8514 57.256C26.9614 57.557 27.1014 57.866 27.2614 58.166C27.4214 58.477 27.5714 58.886 27.7414 59.267C27.8914 59.626 28.0514 60.006 28.2114 60.386C28.3014 60.777 28.4314 61.136 28.5214 61.536C28.6014 61.946 28.6714 62.356 28.7414 62.756C28.7714 62.996 28.8114 63.236 28.8414 63.477H27.4314C27.4014 63.347 27.3714 63.206 27.3314 63.077C27.2114 62.737 27.0714 62.397 26.9414 62.067L26.8904 61.969L26.8804 61.949L26.8504 61.899L26.7904 61.798L26.6704 61.599C26.5904 61.468 26.5004 61.338 26.4204 61.218C26.2504 60.958 26.1004 60.739 25.8904 60.508C25.6904 60.279 25.4604 60.078 25.2704 59.899C24.8904 59.529 24.5004 59.329 24.2404 59.178C24.1104 59.109 23.9804 59.069 23.9104 59.038C23.8304 59.008 23.7904 58.998 23.7904 58.998C23.7904 58.998 23.8304 59.168 23.9204 59.458C23.9904 59.748 24.1504 60.128 24.2604 60.568C24.3304 60.778 24.3904 61.018 24.4904 61.238C24.5804 61.458 24.6404 61.768 24.7404 62.038C24.7904 62.178 24.8504 62.328 24.9004 62.468L24.9804 62.689L25.0304 62.789L25.0404 62.839C25.0804 63.049 25.1404 63.259 25.1804 63.48H4.20036C1.89036 58.069 0.620361 52.109 0.620361 45.869C0.620361 21.019 20.7704 0.869034 45.6204 0.869034C70.4704 0.869034 90.6204 21.019 90.6204 45.869Z" fill="black"/>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<svg width="91" height="91" viewBox="0 0 91 91" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M79.8525 71.7295C78.5125 70.7505 76.7225 69.4395 73.5025 69.2995C73.3325 69.2895 73.1425 69.2896 72.9325 69.2896C72.7525 69.2896 72.5525 69.2895 72.3725 69.2995C69.1725 69.4395 67.3425 70.7495 66.0125 71.7295C64.9925 72.4505 64.3725 72.8895 63.1525 73.0095C62.9725 73.0195 62.7835 73.0195 62.5835 73.0195C62.3935 73.0195 62.1925 73.0195 62.0335 73.0095C60.8125 72.8895 60.1825 72.4495 59.1735 71.7295C57.8235 70.7505 56.0135 69.4395 52.8035 69.2995C52.6145 69.2895 52.4335 69.2896 52.2235 69.2896C52.0345 69.2896 51.8535 69.2895 51.6735 69.2995C48.4735 69.4395 46.6525 70.7495 45.3035 71.7295C44.2935 72.4505 43.6635 72.8895 42.4435 73.0095C42.2835 73.0195 42.0835 73.0195 41.8935 73.0195C41.6835 73.0195 41.5035 73.0195 41.3235 73.0095C40.1035 72.8895 39.4835 72.4495 38.4635 71.7295C37.1235 70.7505 35.3035 69.4395 32.1035 69.2995C31.9235 69.2895 31.7135 69.2896 31.5335 69.2896C31.3435 69.2896 31.1435 69.2895 30.9635 69.2995C27.7635 69.4395 25.9535 70.7495 24.6035 71.7295C23.6035 72.4505 22.9635 72.8895 21.7435 73.0095C21.5635 73.0195 21.3835 73.0195 21.1735 73.0195C20.9835 73.0195 20.7835 73.0195 20.6235 73.0095C19.4035 72.8895 18.7735 72.4495 17.7635 71.7295C16.4135 70.7505 14.6135 69.4395 11.4035 69.2995C11.2635 69.2895 11.1235 69.2896 7.0835 69.2896C9.2035 72.7396 11.7835 75.8696 14.7335 78.6096C15.3635 78.7496 16.0435 78.8385 16.7935 78.8695C17.0035 78.8995 17.2135 78.8996 17.4235 78.8996C17.6335 78.8996 17.8635 78.8995 18.0635 78.8695C21.6535 78.7195 23.6935 77.2595 25.2135 76.1695C26.3435 75.3595 27.0335 74.8495 28.3935 74.7295C28.6035 74.7195 28.8235 74.7196 29.0435 74.7196C29.2535 74.7196 29.4835 74.7195 29.6835 74.7295C31.0435 74.8495 31.7335 75.3605 32.8635 76.1695C34.3835 77.2595 36.4235 78.7195 40.0135 78.8695C40.2135 78.8995 40.4235 78.8996 40.6535 78.8996C40.8635 78.8996 41.0635 78.8995 41.2735 78.8695C44.8735 78.7195 46.9025 77.2595 48.4235 76.1695C49.5535 75.3595 50.2635 74.8495 51.6235 74.7295C51.8035 74.7195 52.0335 74.7196 52.2435 74.7196C52.4825 74.7196 52.6825 74.7195 52.8835 74.7295C54.2535 74.8495 54.9635 75.3605 56.0935 76.1695C57.6145 77.2595 59.6435 78.7195 63.2435 78.8695C63.4425 78.8995 63.6425 78.8996 63.8635 78.8996C64.0835 78.8996 64.3025 78.8995 64.5035 78.8695C68.0935 78.7195 70.1335 77.2595 71.6335 76.1695C72.7735 75.3595 73.4635 74.8495 74.8335 74.7295C75.0335 74.7195 75.2325 74.7196 75.4735 74.7196C75.7145 74.7196 75.9135 74.7195 76.1145 74.7295C77.2635 74.8305 77.9335 75.2095 78.8035 75.8095C79.7135 74.7995 80.5745 73.7495 81.3935 72.6695C80.8925 72.4495 80.4425 72.1395 79.8525 71.7295Z" fill="white"/>
|
|
3
|
+
<path d="M63.8625 85.6295C59.8825 85.6295 57.6915 84.0695 56.0915 82.9095C54.8015 81.9895 54.0715 81.4495 52.2415 81.4495C50.4515 81.4495 49.7115 81.9895 48.4215 82.9095C46.8115 84.0585 44.6315 85.6295 40.6515 85.6295C36.6615 85.6295 34.4615 84.0695 32.8615 82.9095C31.5615 81.9895 30.8515 81.4495 29.0315 81.4495C27.2215 81.4495 26.5015 81.9895 25.2115 82.9095C24.5015 83.4195 23.6815 84.0095 22.6315 84.4995C29.3025 88.4195 37.0825 90.6695 45.3825 90.6695C53.0625 90.6695 60.2925 88.7395 66.6225 85.3495C65.8125 85.5295 64.9025 85.6295 63.8625 85.6295Z" fill="white"/>
|
|
4
|
+
<path d="M36.6125 58.9795C36.7725 59.5495 36.9125 61.4295 37.0225 63.2805H34.8225C34.7825 61.7005 34.7925 60.1305 35.0025 59.5195C35.4725 58.0995 35.4025 56.7595 35.2625 56.0095C35.1325 55.2695 35.9425 49.2695 36.2825 48.4595C36.6125 47.6595 36.6125 47.6595 36.6125 47.6595L36.8825 48.9995C36.8825 48.9995 37.0225 54.2595 36.7525 55.1995C36.4825 56.1495 36.2825 57.7695 36.6125 58.9795Z" fill="white"/>
|
|
5
|
+
<path d="M90.3825 45.6695C90.3825 53.2495 88.5025 60.3895 85.1925 66.6595L85.1825 66.6695C84.6825 66.8895 84.1225 67.0095 83.3025 67.0095C81.6825 67.0095 81.0425 66.5395 79.8635 65.7095C78.4535 64.6695 76.5125 63.2795 72.9425 63.2795C69.3825 63.2795 67.4225 64.6695 66.0125 65.7095C64.8425 66.5395 64.2125 67.0095 62.5825 67.0095C60.9725 67.0095 60.3325 66.5395 59.1725 65.7095C57.7425 64.6695 55.7925 63.2795 52.2225 63.2795H51.1525C51.2135 62.8995 51.2725 62.5085 51.3125 62.0985C51.3925 61.6685 51.4825 61.2185 51.5625 60.7685C51.6525 60.3385 51.7835 59.9385 51.8925 59.5185C51.9725 59.3195 52.0525 59.1195 52.1325 58.9285C52.2225 58.7085 52.3025 58.4985 52.3925 58.2885C52.5735 57.8785 52.6825 57.4185 52.8525 57.0885C53.0335 56.7485 53.1635 56.3995 53.3125 56.0785C53.4325 55.7585 53.5525 55.4485 53.6925 55.1985C53.8125 54.9275 53.9325 54.6985 54.0125 54.4885C54.1725 54.1485 54.2725 53.9185 54.2925 53.8685C54.2715 53.8785 54.2225 53.9085 54.1425 53.9485C54.0335 53.9985 53.8725 54.0785 53.6925 54.2085C53.3425 54.4785 52.8025 54.8285 52.2935 55.4085C52.0435 55.6985 51.7325 55.9985 51.4535 56.3585C51.1635 56.7085 50.9635 57.0685 50.7235 57.4675C50.6035 57.6575 50.4835 57.8585 50.3645 58.0575C50.3035 58.1585 50.2445 58.2575 50.1935 58.3685L50.1035 58.5175L50.0635 58.5975L50.0435 58.6375L50.0035 58.7175L49.9935 58.7475C49.8125 59.2265 49.6025 59.7265 49.4535 60.2265C49.3235 60.7265 49.2035 61.2265 49.1025 61.7165C49.0125 62.2065 48.9425 62.6865 48.8825 63.1565C48.8825 63.1965 48.8725 63.2365 48.8725 63.2765H47.2625C47.3235 62.6755 47.3925 62.0465 47.4625 61.3965C47.5525 60.7765 47.6425 60.1355 47.7325 59.4965C47.8525 58.8665 47.9725 58.2165 48.0925 57.5765C48.2525 56.9565 48.4625 56.3665 48.6225 55.7565C48.8325 55.2165 49.1125 54.5275 49.3515 53.9765C49.6225 53.4365 49.8625 52.9165 50.1315 52.4165C50.4005 51.9165 50.6115 51.4165 50.8615 50.9765C51.3105 50.0675 51.6905 49.2965 51.9515 48.7665C52.2115 48.2065 52.3615 47.8765 52.3615 47.8765V47.8695C52.3615 47.8695 52.0205 48.0185 51.5015 48.3695C50.9715 48.7195 50.2915 49.2995 49.5715 50.0895C49.2115 50.4795 48.8615 50.9495 48.5015 51.4395C48.1415 51.9395 47.8415 52.5305 47.4915 53.1205C47.1715 53.7205 46.9115 54.2505 46.5815 54.9305C46.3315 55.6115 46.0715 56.3105 45.8615 57.0105C45.7115 57.7105 45.5215 58.4015 45.3915 59.0905C45.2815 59.7805 45.1815 60.4605 45.0915 61.1115C45.0115 61.8715 44.9415 62.6005 44.8915 63.2815H43.3215C43.3825 62.6695 43.4125 61.9995 43.4925 61.3095C43.5825 60.5195 43.6725 59.6795 43.7625 58.8205C43.8825 57.9695 44.0125 57.0905 44.1425 56.1895C44.3125 55.3205 44.4925 54.4195 44.6725 53.5305C44.8925 52.6705 45.1725 51.8305 45.4125 50.9795C45.7625 50.1505 46.1125 49.3305 46.4535 48.5305C46.8235 47.7805 47.1635 47.0405 47.5225 46.3405C47.9025 45.6505 48.2225 44.9705 48.5735 44.3705C49.2435 43.1605 49.8435 42.1505 50.2725 41.4505C50.7035 40.7405 50.9625 40.3405 50.9625 40.3405C50.9625 40.3405 50.5525 40.5905 49.9125 41.1305C49.2715 41.6805 48.3915 42.4905 47.4725 43.6105C46.9825 44.1405 46.5425 44.7905 46.0525 45.4605C45.5625 46.1205 45.1525 46.9205 44.6625 47.7005C44.2325 48.5105 43.8925 49.2805 43.4525 50.1895C43.1425 51.1105 42.7725 52.0405 42.5025 52.9795C42.2925 53.9195 42.0625 54.8705 41.8725 55.7995C41.7325 56.7395 41.6025 57.6595 41.4725 58.5495C41.2825 60.2795 41.1525 61.8895 41.1025 63.2795H38.1625C38.0925 61.4095 38.0725 59.5185 38.2325 59.0495C38.5725 58.0395 38.4325 56.7595 38.1025 55.8095C37.7625 54.8705 37.8925 52.5095 38.0325 50.0095C38.1625 47.5195 39.5825 42.1895 39.5825 42.1895C39.5825 42.1895 43.8325 36.7995 45.9225 35.5895C48.0125 34.3695 53.6725 29.7195 52.0525 23.1795C50.4325 16.6395 47.6025 17.5195 46.0525 17.0495C44.5025 16.5795 44.6325 13.8795 46.0525 13.6795C47.4725 13.4795 51.4425 13.2095 51.4425 13.2095C51.4425 13.2095 52.7225 13.9495 56.7035 14.4195C60.6825 14.8895 62.0935 14.5595 62.0935 14.5595C62.0935 14.5595 59.4035 12.8695 56.5735 12.1995C53.7325 11.5195 51.1735 10.4395 51.1735 10.4395C51.1735 10.4395 48.0035 7.94954 45.4435 8.95954C42.8835 9.96954 40.7335 11.8595 41.2635 15.3595C41.8035 18.8695 43.8335 20.8195 45.8535 21.1595C47.8735 21.4995 48.8835 22.3795 48.4825 25.1395C48.1425 27.4695 46.5225 27.9695 45.8535 27.6995C45.1735 27.4295 42.9535 24.7995 40.6635 24.7295C38.3635 24.6695 36.4835 25.3395 31.3635 31.0695C26.2335 36.7995 23.4735 41.6595 21.9835 44.8195C20.5035 47.9895 18.7535 54.1895 18.6835 56.2095C18.6135 58.2395 19.6335 57.8995 20.4335 56.6885C21.2435 55.4685 23.4035 52.0395 25.0235 51.0185C26.6435 50.0085 34.5935 45.4985 34.5935 45.4985C34.5935 45.4985 35.2635 46.9775 35.0635 48.9285C34.8635 50.8885 34.3935 54.1285 33.9235 55.3385C33.4435 56.5485 33.3135 58.3085 33.5835 59.9885C33.6935 60.6485 33.7635 61.9375 33.8235 63.2785H31.0235C30.9635 62.9085 30.9035 62.5385 30.8435 62.1585C30.7535 61.6985 30.6235 61.2275 30.5135 60.7675C30.3735 60.2975 30.1935 59.8185 30.0135 59.3585L29.9635 59.2485L29.9435 59.2185L29.9035 59.1385L29.8235 58.9985L29.6635 58.7185C29.5535 58.5295 29.4535 58.3385 29.3435 58.1585C29.1235 57.7885 28.9735 57.4375 28.7035 57.0975C28.4435 56.7675 28.1835 56.4575 27.9235 56.1985C27.4135 55.6585 26.8835 55.3475 26.5135 55.1375C26.1335 54.9375 25.8835 54.8775 25.8835 54.8775H25.8735C25.8735 54.8775 25.9435 55.1375 26.0635 55.5375C26.1235 55.7375 26.2035 55.9675 26.3035 56.2265C26.4135 56.4765 26.5135 56.7565 26.6135 57.0565C26.7235 57.3575 26.8635 57.6665 27.0235 57.9665C27.1835 58.2775 27.3335 58.6865 27.5035 59.0675C27.6535 59.4265 27.8135 59.8065 27.9735 60.1865C28.0635 60.5775 28.1935 60.9365 28.2835 61.3365C28.3635 61.7465 28.4335 62.1565 28.5035 62.5565C28.5335 62.7965 28.5735 63.0365 28.6035 63.2775H27.1935C27.1635 63.1475 27.1335 63.0065 27.0935 62.8775C26.9735 62.5375 26.8335 62.1975 26.7035 61.8675L26.6525 61.7695L26.6425 61.7495L26.6125 61.6995L26.5525 61.5985L26.4325 61.3995C26.3525 61.2685 26.2625 61.1385 26.1825 61.0185C26.0125 60.7585 25.8625 60.5395 25.6525 60.3085C25.4525 60.0795 25.2225 59.8785 25.0325 59.6995C24.6525 59.3295 24.2625 59.1295 24.0025 58.9785C23.8725 58.9095 23.7425 58.8695 23.6725 58.8385C23.5925 58.8085 23.5525 58.7985 23.5525 58.7985C23.5525 58.7985 23.5925 58.9685 23.6825 59.2585C23.7525 59.5485 23.9125 59.9285 24.0225 60.3685C24.0925 60.5785 24.1525 60.8185 24.2525 61.0385C24.3425 61.2585 24.4025 61.5685 24.5025 61.8385C24.5525 61.9785 24.6125 62.1285 24.6625 62.2685L24.7425 62.4895L24.7925 62.5895L24.8025 62.6395C24.8425 62.8495 24.9025 63.0595 24.9425 63.2805H3.96251C1.65251 57.8695 0.382507 51.9095 0.382507 45.6695C0.382507 20.8195 20.5325 0.66954 45.3825 0.66954C70.2325 0.66954 90.3825 20.8195 90.3825 45.6695Z" fill="white"/>
|
|
6
|
+
</svg>
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
|
|
2
|
+
## Manual Communication with [`env.executeCommands`][wetlands.environment.Environment.executeCommands]
|
|
3
|
+
|
|
4
|
+
This example shows how to use Wetlands to run a specific script within the environment and manage the communication manually using Python's [`multiprocessing.connection`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.connection.Connection). This gives you full control over the interaction protocol but requires more setup.
|
|
5
|
+
|
|
6
|
+
Let's see the main script [`advanced_example.py`](https://github.com/arthursw/wetlands/blob/main/examples/advanced_example.py) step by step.
|
|
7
|
+
|
|
8
|
+
### Initialize Wetlands and Logging
|
|
9
|
+
|
|
10
|
+
We import necessary modules, including [`Client`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.connection.Client) for manual connection and standard Python libraries like [`subprocess`](https://docs.python.org/3/library/subprocess.html#module-subprocess), [`threading`](https://docs.python.org/3/library/threading.html), and [`logging`](https://docs.python.org/3/library/logging.html). We also enable debug logging for Wetlands to see more internal details and initialize the [`EnvironmentManager`][wetlands.environment_manager.EnvironmentManager].
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
# main_script_manual.py
|
|
14
|
+
from multiprocessing.connection import Client
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
import threading
|
|
18
|
+
import logging
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
import time
|
|
21
|
+
|
|
22
|
+
from wetlands.environment_manager import EnvironmentManager
|
|
23
|
+
from wetlands import logger
|
|
24
|
+
|
|
25
|
+
logger.setLogLevel(logging.DEBUG)
|
|
26
|
+
environmentManager = EnvironmentManager("micromamba/")
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Create the Environment
|
|
30
|
+
|
|
31
|
+
Similar to the first example, we create the environment (`advanced_cellpose_env`) and specify its dependencies.
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
deps = {"conda": ["cellpose==3.1.0"]}
|
|
35
|
+
env = environmentManager.create("advanced_cellpose_env", deps)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Execute a Custom Script in the Environment
|
|
39
|
+
|
|
40
|
+
Instead of [`env.launch()`][wetlands.environment.Environment.launch], we use [`env.executeCommands()`][wetlands.environment.Environment.executeCommands]. This method allows us to run arbitrary shell commands within the activated environment. Here, we execute a specific Python script ([`advanced_example_module.py`](https://github.com/arthursw/wetlands/blob/main/examples/advanced_example_module.py)) using `python -u` (unbuffered output, important for reading stdout line-by-line immediately). We capture the `Popen` object for the launched process. We also redirect stderr to stdout for easier log capture.
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
print("Executing advanced_example_module.py in environment...")
|
|
44
|
+
process = env.executeCommands(["python -u advanced_example_module.py"])
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Establish Manual Connection
|
|
48
|
+
|
|
49
|
+
The script we just launched (`advanced_example_module.py`) is designed to start a server and print the port it's listening on to its standard output. Our main script now needs to read the `stdout` of the `process` launched by Wetlands to discover this port number. We loop through the output lines until we find the line indicating the port.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
port = None
|
|
53
|
+
if process.stdout is None:
|
|
54
|
+
print("Process has no stdout stream.", file=sys.stderr)
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
print("Waiting for environment process to report listening port...")
|
|
57
|
+
for line in process.stdout:
|
|
58
|
+
if line.strip().startswith("Listening port "):
|
|
59
|
+
port = int(line.strip().replace("Listening port ", ""))
|
|
60
|
+
break
|
|
61
|
+
|
|
62
|
+
print(f"Connecting to localhost:{port}...")
|
|
63
|
+
connection = Client(("localhost", port))
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Log Environment Output (Optional)
|
|
67
|
+
|
|
68
|
+
To see ongoing output from the script running in the environment, we can start a background thread that continuously reads and prints lines from the process's stdout.
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
def log_output(proc: subprocess.Popen):
|
|
72
|
+
if proc.stdout:
|
|
73
|
+
for line_bytes in iter(proc.stdout.readline, b''):
|
|
74
|
+
print(f"[Env Output]: {line_bytes.decode().strip()}")
|
|
75
|
+
|
|
76
|
+
output_thread = threading.Thread(target=log_output, args=(process,), daemon=True)
|
|
77
|
+
output_thread.start()
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Send Commands and Receive Results Manually
|
|
81
|
+
|
|
82
|
+
Now that we have a direct `connection` object (from [`multiprocessing.connection.Client`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.connection.Client)), we can implement our own communication protocol. We send dictionaries containing an `action`, `function` name, and `args`. We then wait (`connection.recv()`) for a response dictionary from the server script running in the environment.
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
imagePath = "cellpose_img02.png"
|
|
86
|
+
|
|
87
|
+
print(f"Sending command: download image {imagePath}")
|
|
88
|
+
connection.send(dict(action="execute", function="downloadImage", args=[imagePath]))
|
|
89
|
+
result = connection.recv()
|
|
90
|
+
print(f"Received response: {result}")
|
|
91
|
+
|
|
92
|
+
segmentationPath = "cellpose_img02_segmentation.png"
|
|
93
|
+
print(f"Sending command: segment image {imagePath}")
|
|
94
|
+
args = [str(imagePath), str(segmentationPath)]
|
|
95
|
+
connection.send(dict(action="execute", function="segmentImage", args=args))
|
|
96
|
+
result = connection.recv()
|
|
97
|
+
print(f"Received response: {result}")
|
|
98
|
+
if 'diameters' in result:
|
|
99
|
+
print(f"Object diameters: {result['diameters']}")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Tell the Environment Process to Exit and clean up
|
|
103
|
+
|
|
104
|
+
We send a custom 'exit' message according to our protocol. The server script is designed to shut down upon receiving this message.
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
print("Sending exit command...")
|
|
108
|
+
connection.send(dict(action="exit"))
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
We close our client-side connection and wait for the process we launched with `executeCommands` to terminate.
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
connection.close()
|
|
115
|
+
process.wait(timeout=10)
|
|
116
|
+
if process.returncode is None:
|
|
117
|
+
process.kill()
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
Now, let's examine the `advanced_example_module.py` script, which is executed by Wetlands in the isolated environment via `executeCommands`.
|
|
123
|
+
|
|
124
|
+
**Define Callable Functions**
|
|
125
|
+
|
|
126
|
+
This script defines the functions (`downloadImage`, `segmentImage`) that the main script will invoke remotely. These functions perform the actual work (downloading, segmenting using `example_module`) *inside the environment* and use the provided `connection` object to send back results or status messages.
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
# advanced_example_module.py
|
|
130
|
+
import sys
|
|
131
|
+
import urllib.request
|
|
132
|
+
from multiprocessing.connection import Listener
|
|
133
|
+
from pathlib import Path
|
|
134
|
+
import example_module # Reuse logic from the simple example module
|
|
135
|
+
|
|
136
|
+
def downloadImage(imagePath_str, connection):
|
|
137
|
+
"""Downloads the image *inside* the environment."""
|
|
138
|
+
imagePath = Path(imagePath_str)
|
|
139
|
+
imageUrl = "https://www.cellpose.org/static/images/img02.png"
|
|
140
|
+
print(f"[Inside Env] Downloading image to {imagePath}...")
|
|
141
|
+
try:
|
|
142
|
+
with urllib.request.urlopen(imageUrl) as response:
|
|
143
|
+
imageData = response.read()
|
|
144
|
+
with open(imagePath, "wb") as handler:
|
|
145
|
+
handler.write(imageData)
|
|
146
|
+
print("[Inside Env] Image downloaded.")
|
|
147
|
+
connection.send(dict(status="success", message="Image downloaded."))
|
|
148
|
+
except Exception as e:
|
|
149
|
+
print(f"[Inside Env] Error downloading image: {e}")
|
|
150
|
+
connection.send(dict(status="error", message=str(e)))
|
|
151
|
+
|
|
152
|
+
def segmentImage(imagePath_str, segmentationPath_str, connection):
|
|
153
|
+
"""Runs segmentation *inside* the environment."""
|
|
154
|
+
imagePath = Path(imagePath_str)
|
|
155
|
+
segmentationPath = Path(segmentationPath_str)
|
|
156
|
+
print(f"[Inside Env] Segmenting {imagePath}...")
|
|
157
|
+
try:
|
|
158
|
+
diameters = example_module.segment(imagePath, segmentationPath)
|
|
159
|
+
print("[Inside Env] Segmentation complete.")
|
|
160
|
+
connection.send(dict(status="success", message="Image segmented.", diameters=diameters))
|
|
161
|
+
except Exception as e:
|
|
162
|
+
print(f"[Inside Env] Error during segmentation: {e}")
|
|
163
|
+
connection.send(dict(status="error", message=str(e)))
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Set Up the Server**
|
|
167
|
+
|
|
168
|
+
The main part of the script uses [`multiprocessing.connection.Listener`](https://docs.python.org/3/library/multiprocessing.html#multiprocessing.connection.Listener) to create a server socket listening on `localhost` and an OS-assigned port (`0`). **Crucially, it prints the chosen port number to standard output**, which is how the main script discovers where to connect. It then waits for the main script to connect (`listener.accept()`).
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
|
|
172
|
+
with Listener(("localhost", 0)) as listener:
|
|
173
|
+
# Print the port for the main process to read
|
|
174
|
+
print(f"Listening port {listener.address[1]}", flush=True)
|
|
175
|
+
with listener.accept() as connection:
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Process Incoming Messages**
|
|
179
|
+
|
|
180
|
+
Once connected, the script enters a loop, waiting to receive messages (`connection.recv()`). It parses the received dictionary, checks the `action`, and calls the corresponding local function (`downloadImage` or `segmentImage`) if the action is `execute`. If the action is `exit`, it sends a confirmation and terminates the script (`sys.exit(0)`).
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
while message := connection.recv():
|
|
184
|
+
if message["action"] == "execute":
|
|
185
|
+
locals()[message["function"]](*(message["args"] + [connection]))
|
|
186
|
+
if message["action"] == "exit":
|
|
187
|
+
connection.send(dict(action="Exited."))
|
|
188
|
+
sys.exit(0)
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Summary of Example 2 Flow:**
|
|
193
|
+
|
|
194
|
+
The main script uses [`EnvironmentManager`][wetlands.environment_manager.EnvironmentManager] to create an environment. [`env.executeCommands()`][wetlands.environment.Environment.executeCommands] starts a *custom* server script (`advanced_example_module.py`) inside the environment. The main script reads the server's port from stdout and connects manually using `Client`. Communication happens via custom message dictionaries sent over this connection. The main script explicitly tells the server to exit before cleaning up the process started by [`executeCommands`][wetlands.environment.Environment.executeCommands]. This approach offers more control but requires implementing the server logic and communication protocol.
|