coconet-python 0.4.4__py3-none-any.whl

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.
coconet/netlogo.py ADDED
@@ -0,0 +1,64 @@
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ from dataclasses import dataclass, field
5
+ from typing import cast
6
+
7
+ import numpy as np
8
+
9
+
10
+ def nl_round(value: float) -> int:
11
+ if value >= 0:
12
+ return math.floor(value + 0.5)
13
+ return math.ceil(value - 0.5)
14
+
15
+
16
+ def nl_ceiling(value: float) -> int:
17
+ return math.ceil(value)
18
+
19
+
20
+ def nl_median(a: float, b: float, c: float) -> float:
21
+ # NetLogo's median of three numbers. Profiling showed np.median on a fresh
22
+ # length-3 array dominated spawn/consume/cyclone hot paths; a tiny sort network
23
+ # matches the same middle-element result for three scalars without allocation.
24
+ if a > b:
25
+ a, b = b, a
26
+ if b > c:
27
+ b, c = c, b
28
+ if a > b:
29
+ a, b = b, a
30
+ return float(b)
31
+
32
+
33
+ def heading_from_dx_dy(dx: np.ndarray, dy: np.ndarray) -> np.ndarray:
34
+ """NetLogo heading in degrees clockwise from north."""
35
+ heading = (np.degrees(np.arctan2(dx, dy)) + 360.0) % 360.0
36
+ return cast(np.ndarray, heading)
37
+
38
+
39
+ @dataclass(slots=True)
40
+ class NetLogoRng:
41
+ seed_value: int = 1
42
+ _rs: np.random.RandomState = field(init=False, repr=False)
43
+
44
+ def __post_init__(self) -> None:
45
+ self._rs = np.random.RandomState(self.seed_value)
46
+
47
+ def seed(self, value: int) -> None:
48
+ self.seed_value = int(value)
49
+ self._rs.seed(self.seed_value)
50
+
51
+ def random_float(self, upper: float = 1.0) -> float:
52
+ return float(self._rs.random_sample() * upper)
53
+
54
+ def random_int(self, upper: float) -> int:
55
+ n = math.floor(upper)
56
+ if n <= 0:
57
+ return 0
58
+ return int(self._rs.randint(0, n))
59
+
60
+ def one_of(self, indices: np.ndarray) -> int | None:
61
+ if indices.size == 0:
62
+ return None
63
+ idx = self._rs.randint(0, indices.size)
64
+ return int(indices[idx])
coconet/py.typed ADDED
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,275 @@
1
+ Metadata-Version: 2.4
2
+ Name: coconet-python
3
+ Version: 0.4.4
4
+ Summary: Headless Python port of the legacy CoCoNet NetLogo model.
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: numpy>=2.2.0
7
+ Requires-Dist: pandas>=2.2.0
8
+ Requires-Dist: pyyaml>=6.0.0
9
+ Requires-Dist: threadpoolctl>=3.5.0
10
+ Provides-Extra: profile
11
+ Requires-Dist: pyinstrument>=5.0.0; extra == 'profile'
12
+ Description-Content-Type: text/markdown
13
+
14
+ # CoCoNet Python Port
15
+
16
+ This repository contains a headless Python port of the legacy NetLogo CoCoNet model.
17
+
18
+ **User documentation (Jekyll):** the [`documentation/`](documentation/) directory holds a static site with installation, configuration, API, and I/O reference. On GitHub, enable **Settings → Pages → GitHub Actions**; pushes to `main` then publish to `https://<owner>.github.io/<repo>/` via the “Deploy documentation to GitHub Pages” workflow.
19
+
20
+ ## Two ways to run CoCoNet
21
+
22
+ The same simulation engine is exposed for **shell-oriented workflows** and for **programmatic control**. Pick the entry point that matches how you are integrating the model.
23
+
24
+ ### 1. Command-line interface (CLI)
25
+
26
+ Use this when you want **files, flags, and environment variables**—for example local terminals, Docker (the published image uses the CLI as its entrypoint), shell scripts, or CI jobs that only need to pass paths.
27
+
28
+ - **Console script:** `coconet` (installed with the package).
29
+ - **Module form:** `python -m coconet` (same behaviour as `coconet`).
30
+ - **Typical inputs:** `--config` (YAML), `--parameter-file` (legacy CSV), `--reefs-file`, `--coastline-file`, `--output-file`, plus `COCONET_*` environment overrides. See **`coconet --help`** and the [CLI documentation](documentation/cli.md).
31
+
32
+ The CLI handles **logging bootstrap**, optional **CPU profiling** (`--profile`, extra dependency), and applies a fixed set of **command-line overrides** on top of the shared configuration loader.
33
+
34
+ ### 2. Python library (PyPI package **coconet-python**)
35
+
36
+ Use this when you need **full control from Python**—custom CLIs, web services, notebooks, schedulers, or multi-step pipelines. Install from **PyPI** (distribution name **`coconet-python`**; import package remains **`coconet`**):
37
+
38
+ ```bash
39
+ pip install coconet-python
40
+ ```
41
+
42
+ The stable library surface is **`load_coconet_config`** and **`run_coconet`** in `coconet.api` (re-exported from `coconet`). Build a `CoconetConfig` from YAML paths, legacy CSV, environment, an optional **`scenario`** dict, and keyword overrides, then run:
43
+
44
+ ```python
45
+ from coconet import load_coconet_config, run_coconet
46
+
47
+ cfg = load_coconet_config(
48
+ config_file="config/example.yaml",
49
+ reefs_file="legacy/reefs2024.csv",
50
+ coastline_file="legacy/coastline.csv",
51
+ output_file="out/run.csv",
52
+ )
53
+ run_coconet(cfg, configure_logs=True)
54
+ ```
55
+
56
+ For **advanced** embedding you can still construct **`CoconetModel`** directly from **`CoconetConfig`**. See the [Python API](documentation/python-api.md) page for precedence, logging, and `__all__`.
57
+
58
+ ## Why this exists
59
+
60
+ The `legacy/` directory contains the original monolithic NetLogo model (`CoCoNet V3_rubble.nlogo`) and its data files. This port migrates the model to a modern Python stack for:
61
+
62
+ - headless execution in cloud/CI environments,
63
+ - typed, maintainable code,
64
+ - easier configuration through files and environment variables,
65
+ - reproducible scenario runs without a GUI.
66
+
67
+ ## Project layout
68
+
69
+ - `legacy/` - original NetLogo model and data files.
70
+ - `coconet/` - Python implementation:
71
+ - `config.py` - typed configuration and legacy parameter CSV parsing,
72
+ - `model.py` - simulation engine (ported procedures),
73
+ - `api.py` - **library** entry points (`load_coconet_config`, `run_coconet`) for PyPI consumers and embedders,
74
+ - `cli.py` - **CLI** entry point (`coconet` / `python -m coconet`),
75
+ - `__main__.py` - delegates to the CLI for `python -m coconet`.
76
+
77
+ ## Running with uv (from a git checkout)
78
+
79
+ ```bash
80
+ uv sync
81
+ uv run coconet --parameter-file legacy/I_2p6.csv --output-file output.csv
82
+ ```
83
+
84
+ You can also use YAML config:
85
+
86
+ ```bash
87
+ uv run coconet --config config/example.yaml --output-file output.csv
88
+ ```
89
+
90
+ ## Docker (GitHub Container Registry)
91
+
92
+ A container image is built with [GitHub Actions](.github/workflows/docker-publish.yml) on pushes to the default branch (`main`) and on SemVer tags `v*`. It is published to **GitHub Container Registry** as [`ghcr.io/gbrrestoration/coconet-python`](https://github.com/gbrrestoration/coconet-python/pkgs/container/coconet-python) (pull: `docker pull ghcr.io/gbrrestoration/coconet-python:latest`).
93
+
94
+ The image is based on `python:3.12-slim-bookworm`, installs dependencies with **uv** (`uv sync --frozen`), bundles `legacy/` and `config/` under `/app`, and runs as UID **1000**. The container entrypoint is the **`coconet` CLI** (see [Two ways to run CoCoNet](#two-ways-to-run-coconet)). If you need the **library** interface instead, add `pip install coconet-python` (or copy the package) in your own image and call `load_coconet_config` / `run_coconet` from your code.
95
+
96
+ ### Input and output paths
97
+
98
+ Point the CLI at inputs and outputs using flags (or YAML / environment variables). For data on the host, mount a host directory into the container and pass **container paths** to the CLI.
99
+
100
+ | Item | CLI flags | Environment (optional) |
101
+ | --- | --- | --- |
102
+ | Reef table CSV | `--reefs-file` | `COCONET_REEFS_FILE` |
103
+ | Coastline CSV | `--coastline-file` | `COCONET_COASTLINE_FILE` |
104
+ | Scenario YAML | `--config` | (YAML keys `reefs_file`, `coastline_file`, …) |
105
+ | Legacy parameter CSV | `--parameter-file` | (set via YAML `parameter_file` if needed) |
106
+ | Run output CSV | `--output-file` | `COCONET_OUTPUT_FILE` |
107
+
108
+ **Example: bundled inputs, outputs on the host**
109
+
110
+ ```bash
111
+ mkdir -p ./out
112
+ docker run --rm \
113
+ -v "$(pwd)/out:/out" \
114
+ ghcr.io/gbrrestoration/coconet-python:latest \
115
+ --config /app/config/example.yaml \
116
+ --output-file /out/run.csv
117
+ ```
118
+
119
+ **Example: custom inputs and outputs on the host**
120
+
121
+ ```bash
122
+ mkdir -p ./out
123
+ docker run --rm \
124
+ -v "/path/to/mydata:/data:ro" \
125
+ -v "$(pwd)/out:/out" \
126
+ ghcr.io/gbrrestoration/coconet-python:latest \
127
+ --reefs-file /data/reefs2024.csv \
128
+ --coastline-file /data/coastline.csv \
129
+ --parameter-file /data/myparams.csv \
130
+ --output-file /out/results.csv
131
+ ```
132
+
133
+ Use `-e COCONET_ENSEMBLE_RUNS=2` (and other `COCONET_*` variables) if you prefer environment overrides; see below. Run `docker run --rm ghcr.io/gbrrestoration/coconet-python:latest --help` for all CLI options.
134
+
135
+ **Permissions:** the process in the image runs as UID **1000** (`coconet`). Writable bind mounts (for `--output-file`, profiling output, etc.) must allow that user to create files—for example `sudo chown 1000:1000 ./out` on the host directory, or a Docker volume instead of a root-owned host path. Overriding with `--user` is not recommended because `/app` in the image is not world-readable.
136
+
137
+ Environment overrides are supported with `COCONET_` prefix, for example:
138
+
139
+ ```bash
140
+ export COCONET_ENSEMBLE_RUNS=2
141
+ export COCONET_END_YEAR=1990
142
+ uv run coconet --parameter-file legacy/I_2p6.csv
143
+ ```
144
+
145
+ ## Logging
146
+
147
+ The CLI includes lifecycle logging for setup, progress, and run completion.
148
+
149
+ You can control logging level with either CLI or environment variables:
150
+
151
+ ```bash
152
+ uv run coconet --config config/example.yaml --log-level DEBUG
153
+ ```
154
+
155
+ ```bash
156
+ export COCONET_LOG_LEVEL=WARNING
157
+ uv run coconet --config config/example.yaml
158
+ ```
159
+
160
+ Supported levels are: `CRITICAL`, `ERROR`, `WARNING`, `INFO`, `DEBUG`.
161
+
162
+ If both are provided, CLI takes precedence over the environment variable.
163
+
164
+ ## Notes on parity
165
+
166
+ The implementation ports the NetLogo procedures and keeps important NetLogo semantics where feasible (seed resets, annual loop structure, reef/site-level state updates, interventions, and output schema). This is designed to support model-output comparison workflows between the legacy and Python implementations.
167
+
168
+ ## Legacy parameter CSV (`--parameter-file`)
169
+
170
+ The documentation site (Jekyll under `documentation/`, page **Scenario parameter semantics**) carries the same tables as the following sections for easier browsing once GitHub Pages is enabled.
171
+
172
+ Rows are `Label, value` pairs. Blank lines are skipped. Parsing is **case-insensitive** on the label. The first row whose first cell is exactly `Ensemble` (the output table header) ends the config block; everything after that is treated as data rows, not parameters.
173
+
174
+ Labels are mapped in `coconet/config.py` (`label_to_attr`). If a label from a template file is **missing**, the value stays at the Python default in `CoconetConfig` (for example `legacy/I_2p6.csv` has no `Spinup backtrack (years)` row, so the default `50` is used, and no `CoTS vessels in active sector` row, so default `0` is used).
175
+
176
+ **YAML / environment-only** (not read from the legacy CSV): `reefs_file`, `coastline_file`, `output_file`, `ensemble_threads`, `log_level`, `search_mode`, `perfect_intervention`, `unregulated_fishing`. Override these via `--config`, `COCONET_*` env vars, or CLI flags (`--reefs-file`, `--coastline-file`, `--output-file`, …) where supported.
177
+
178
+ ### Simulation schedule
179
+
180
+ | Legacy CSV label | `CoconetConfig` field | Role |
181
+ | --- | --- | --- |
182
+ | Climate scenario | `SSP` | Shared socioeconomic pathway used for post-`projection_year` bleaching severity, coral growth `pH` stress (`sqrt(SSP)`), and related scenario branches. Supported values in code: `1.9`, `2.6`, `4.5`, `7.0`, `8.5`. |
183
+ | Ensemble runs | `ensemble_runs` | Number of stochastic ensemble replicates (`1..ensemble_runs`); ensemble `0` is spin-up. |
184
+ | Start year | `start_year` | Calendar year when saved initial conditions are loaded for ensembles `>= 1`. |
185
+ | Spinup backtrack (years) | `spinup_backtrack_years` | For ensemble `0` only, simulation begins at `start_year - spinup_backtrack_years` to warm up the system. |
186
+ | Save year | `save_year` | Initial state for ensembles `>= 1` is taken from the end of the spin-up trajectory in the year before `save_year` (see model loop). |
187
+ | Projection year | `projection_year` | Before this year, historical cyclone/bleaching drivers apply where coded; from this year onward, scenario-based projection drivers apply. |
188
+ | End year | `end_year` | Last calendar year in the main annual loop. |
189
+ | Search year | `search_year` | When `search_mode == 1`, used to switch search behaviour and benefit accumulation (optimization-style mode; default `search_mode` is `0`). |
190
+
191
+ ### Crown-of-thorns (CoTS) control
192
+
193
+ All CoTS control logic runs only when `year >= start_CoTS_control`. Vessel counts set how many control “vessels” are simulated for that annual step; **each non-zero regional/GBR/sector option runs its own control pass** in sequence (separate dive budgets). Intervention reefs are still filtered by region for the regional modes.
194
+
195
+ | Legacy CSV label | `CoconetConfig` field | Role |
196
+ | --- | --- | --- |
197
+ | CoTS control start year | `start_CoTS_control` | First year any CoTS control runs. |
198
+ | CoTS control ecological threshold (CoTS per ha) | `eco_threshold` | Site-level CoTS density above which culling dives target that site; also scales post-cull age-class distribution. |
199
+ | CoTS control CoTS threshold (CoTS per ha) | `CoTS_threshold` | Reef-level mean CoTS (`S_manta_r`) must be **below** this (or coral above `coral_threshold`) for the reef to be eligible for control. |
200
+ | CoTS control coral threshold (CoTS per ha) | `coral_threshold` | If reef mean coral cover exceeds this, the reef is eligible even when CoTS density is high (legacy naming says “CoTS per ha” but the field is compared to coral cover). |
201
+ | CoTS vessels across GBR | `CoTS_vessels_GBR` | Sets vessel count and `control_region = "GBR"` for `control_cots()`. |
202
+ | CoTS vessels in Far-northern Region | `CoTS_vessels_FN` | Same for region `FN`. |
203
+ | CoTS vessels in Northern Region | `CoTS_vessels_N` | Same for region `N`. |
204
+ | CoTS vessels in Central Region | `CoTS_vessels_C` | Same for region `C`. |
205
+ | CoTS vessels in Southern Region | `CoTS_vessels_S` | Same for region `S`. |
206
+ | CoTS vessels in active sector | `CoTS_vessels_sector` | Vessel count for `control_cots_by_sector()`: targets the sector (1–11) with highest mean CoTS; uses a slightly different dive-cost formula than regional control. |
207
+
208
+ ### Catchment, zoning, and fishing
209
+
210
+ | Legacy CSV label | `CoconetConfig` field | Role |
211
+ | --- | --- | --- |
212
+ | Catchment restoration start year | `start_catchment_restore` | First year catchment recovery can proceed. |
213
+ | Catchment restoration timescale (years) | `restore_timeframe` | Each year, `catchment_condition` moves toward `1` by `1/restore_timeframe` (exponential approach). Must be `> 0` for the update to run. Used in cyclone/flood loading (`flood_load`). |
214
+ | Future zoning start year | `start_modified_zoning` | Year assigned as `future_rezone_year` for the first `rezoned_reefs` reefs in priority order (among reefs that still have `rezone_year == 9999`). |
215
+ | Number of reefs included in future rezoning | `rezoned_reefs` | Count of reefs that receive a planned future closure year. |
216
+ | Reduction in fisheries catch start year | `start_modified_fishing` | From this year on, global annual emperor and coral-trout catch caps are multiplied by `1 - catch_reduction`. |
217
+ | Fractional reduction in fisheries catches | `catch_reduction` | Fractional cut applied to `e_annual` and `g_annual` (see `apply_fishing`). |
218
+ | Upper fish size limit start year | `start_upper_sizelimit` | From this year, fishing removes no `E_5` / `G_5` (largest age classes). |
219
+ | Lower fish size limit start year | `start_lower_sizelimit` | From this year, fishing removes no `E_3` / `G_3` (smallest targeted ages). |
220
+ | Exclude fishing from active outbreak reefs start year | `start_CoTSlimit` | From this year, if a weighted CoTS outbreak index on the reef exceeds `68`, all targeted emperor/trout catch on that visit is set to zero. |
221
+
222
+ ### Other species and shading
223
+
224
+ | Legacy CSV label | `CoconetConfig` field | Role |
225
+ | --- | --- | --- |
226
+ | Emperor release start year | `start_emperor_release` | First year `release_fish()` runs. |
227
+ | Number of release reefs | `release_reefs` | Maximum reefs to stock per year (priority order, within intervention lat/lon box). |
228
+ | Maximum adult emperors (per ha) for release | `release_threshold` | Reefs at or above this total adult emperor biomass are **skipped** (release targets depleted reefs). |
229
+ | Number of juvenile emperors released per reef | `release_number` | Juveniles added to `E_1`, spread per reef area (ha). |
230
+ | Regional shading start year | `start_regional_shading` | First year `shade_regional_coral()` runs. |
231
+ | Absolute DHW reduction due to regional shading (DHW) | `regional_shading_reduction` | For reefs inside the intervention bounding box, bleaching DHW is multiplied by `(1 - regional_shading[reef])` after this value is assigned (same multiplicative pattern as local shading; legacy label says “absolute” but the implementation scales DHW). |
232
+
233
+ ### Intervention bounding box
234
+
235
+ Applied to most spatial interventions (not CoTS regional control, which uses reef region codes). Reef centre must satisfy `intervene_lon_min < x < intervene_lon_max` and `intervene_lat_min < y < intervene_lat_max`.
236
+
237
+ | Legacy CSV label | `CoconetConfig` field |
238
+ | --- | --- |
239
+ | Minimum longitude of interventions | `intervene_lon_min` |
240
+ | Maximum longitude of interventions | `intervene_lon_max` |
241
+ | Minimum latitude of interventions | `intervene_lat_min` |
242
+ | Maximum latitude of interventions | `intervene_lat_max` |
243
+
244
+ ### Rubble, coral seeding, slicks, local shading, ocean acidification
245
+
246
+ | Legacy CSV label | `CoconetConfig` field | Role |
247
+ | --- | --- | --- |
248
+ | Rubble consolidation start year | `start_rubble_consolidation` | Enables `consolidate_rubble()`. |
249
+ | Annual number of consolidated reefs | `consolidation_reefs` | Cap on reefs treated per year. |
250
+ | Minimum rubble cover threshold for consolidation [0 1] | `consolidation_threshold` | Reef mean rubble must be **strictly greater** than this to qualify. |
251
+ | Total annual consolidated area (ha) | `consolidation_hectares` | Divided by `ha_per_site` to give per-site rubble removal at a chosen site. |
252
+ | Thermally tolerant coral seeding start year | `start_coral_seeding` | Enables `seed_tt_coral()`. |
253
+ | Annual number of reefs seeded with coral | `seed_reefs` | Max reefs per year. |
254
+ | Maximum coral cover threshold for coral seeding [0 1] | `seed_threshold` | Reefs at or above this mean coral cover are **skipped**. |
255
+ | Total annual area of seeded corals (ha) | `seed_hectares` | Determines fractional cover added per seeding event. |
256
+ | Fraction of staghorn … hybridise … [0 1] | `hybrid_fraction` | In `spawn_corals()`, scales hybridisation of branching (`sa`) with thermally tolerant (`tt`) corals for recruitment composition and thermal traits. |
257
+ | Dominance of thermally tolerant corals … [0 1] | `dominance` | Weights how hybrid thermal tolerance blends toward `tt` vs `sa` in the same hybrid calculation. |
258
+ | Coral slicks start year | `start_coral_slick` | Enables `seed_coral_slick()`. |
259
+ | Annual number of reefs with coral slicks released | `slick_reefs` | Max reefs per year. |
260
+ | Maximum coral cover threshold for coral slicks [0 1] | `slick_threshold` | Reefs with thermally tolerant reef-mean cover `C_r["tt"]` **at or above** this are **skipped**. |
261
+ | Total annual area of slick corals (ha) | `slick_hectares` | Slick fraction per site; material is drawn from a random source site’s community composition. |
262
+ | Reef shading start year | `start_reef_shading` | Enables `shade_local_reef()`. |
263
+ | Annual number of reefs locally shaded | `shading_reefs` | Each chosen reef gets `reef_shading[reef] = reef_shading_reduction`. |
264
+ | Fractional DHW reduction due to local shading [0 1] | `reef_shading_reduction` | Bleaching DHW multiplied by `(1 - reef_shading[reef])`. |
265
+ | Ocean acidification treatment start year | `start_pH_protection` | Enables `increase_pH()`. |
266
+ | Annual number of reefs treated for ocean acidification | `pH_reefs` | Reefs per year receive `pH_protect[reef] = pH_protection`. |
267
+ | Fractional protection from ocean acidification [0 1] | `pH_protection` | From `projection_year` onward, coral growth uses `pH_effect_t = (1 - pH_protect[reef]) * sqrt(SSP)`. |
268
+
269
+ ### Search mode and “perfect” interventions
270
+
271
+ - **`search_mode`** (`0` default): normal output; `1` enables search-year logic and benefit tracking (see `model.py`).
272
+ - **`perfect_intervention`**: string mode applied from year **2026** onward (`_apply_perfect_intervention`), e.g. idealised starfish control or shading (not set via legacy CSV).
273
+ - **`unregulated_fishing`**: if true, sets zoning years so fishing regulation drivers are bypassed.
274
+
275
+ For the exact equations, see the corresponding methods on `CoconetModel` in `coconet/model.py` and the label map in `coconet/config.py`.
@@ -0,0 +1,13 @@
1
+ coconet/__init__.py,sha256=2LOjelsND3TvuWet89-7rNBVprqPvWGhYJdiaulM1Y8,320
2
+ coconet/__main__.py,sha256=zx1ctPZ9odq7xI7eJYYAN9fR0zrCNnsg64zcLl3mA-g,156
3
+ coconet/api.py,sha256=jrXj1l4E8mgFbMBVyWjFKC40SDpZdlRw9tWwJv2jtfk,3554
4
+ coconet/cli.py,sha256=x1cPdy_blPCXFtKqyXRhMCl4W2YqwGTEFM8umY3XdXk,8183
5
+ coconet/config.py,sha256=NYO86gRzBdAScwILjVSfwglYkN1VlqBKU_V0yPFf0wI,9845
6
+ coconet/logging_utils.py,sha256=-dRWsbVxXXZQ7VNl8BO-GwaeKNvLf5h_wRa9tlV4F4Q,801
7
+ coconet/model.py,sha256=kgpvr2PQ6Qt0rtHPF8N1FarU3ZsH-W-QkCX-jIzM7gA,95412
8
+ coconet/netlogo.py,sha256=qRci5YMxmtInoIwNbbsXFWK7iBI7GPfbc4C73ptOmg8,1802
9
+ coconet/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
10
+ coconet_python-0.4.4.dist-info/METADATA,sha256=V1pH-3fkkvrlJoXxWpSG9Hi8zmyotyjIeFjHQhh8emA,18725
11
+ coconet_python-0.4.4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
12
+ coconet_python-0.4.4.dist-info/entry_points.txt,sha256=ckzGfIMqKLM2O95uEEv81N3lKC2PfCydWdTmUpHIib0,45
13
+ coconet_python-0.4.4.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ coconet = coconet.cli:main