imexp 0.1.0__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.
@@ -0,0 +1,35 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ${{ matrix.os }}
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ os:
14
+ - ubuntu-latest
15
+ - macos-latest
16
+ - windows-latest
17
+ steps:
18
+ - uses: actions/checkout@v6
19
+ - uses: actions/setup-python@v6
20
+ with:
21
+ python-version: "3.12"
22
+ - run: python -m pip install --upgrade pip
23
+ - run: python -m pip install -e .[dev]
24
+ - run: python -m pytest -q
25
+
26
+ package:
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: actions/checkout@v6
30
+ - uses: actions/setup-python@v6
31
+ with:
32
+ python-version: "3.12"
33
+ - run: python -m pip install --upgrade pip build
34
+ - run: python packaging/clean_build_artifacts.py
35
+ - run: python -m build --sdist --wheel
@@ -0,0 +1,93 @@
1
+ name: Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ push:
6
+ tags:
7
+ - "v*"
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ sdist:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ - uses: actions/setup-python@v6
18
+ with:
19
+ python-version: "3.12"
20
+ - run: python -m pip install --upgrade pip build
21
+ - run: python packaging/clean_build_artifacts.py
22
+ - run: python -m build --sdist
23
+ - uses: actions/upload-artifact@v7
24
+ with:
25
+ name: dist-sdist
26
+ path: dist/*
27
+
28
+ wheel:
29
+ runs-on: ${{ matrix.runner }}
30
+ strategy:
31
+ fail-fast: false
32
+ matrix:
33
+ include:
34
+ - runner: macos-15-intel
35
+ target: macos-x86_64
36
+ - runner: macos-14
37
+ target: macos-arm64
38
+ - runner: windows-latest
39
+ target: windows-x86_64
40
+ steps:
41
+ - uses: actions/checkout@v6
42
+ - uses: actions/setup-python@v6
43
+ with:
44
+ python-version: "3.12"
45
+ - run: python -m pip install --upgrade pip build
46
+ - run: python packaging/clean_build_artifacts.py
47
+ - run: python packaging/bundle_exporter.py --target "${{ matrix.target }}"
48
+ - run: python -m build --wheel
49
+ - uses: actions/upload-artifact@v7
50
+ with:
51
+ name: dist-${{ matrix.target }}
52
+ path: dist/*
53
+
54
+ publish-testpypi:
55
+ if: github.event_name == 'workflow_dispatch'
56
+ needs:
57
+ - sdist
58
+ - wheel
59
+ runs-on: ubuntu-latest
60
+ environment:
61
+ name: testpypi
62
+ permissions:
63
+ id-token: write
64
+ steps:
65
+ - uses: actions/download-artifact@v8
66
+ with:
67
+ path: dist
68
+ merge-multiple: true
69
+ - uses: pypa/gh-action-pypi-publish@release/v1
70
+ with:
71
+ repository-url: https://test.pypi.org/legacy/
72
+ packages-dir: dist
73
+ skip-existing: true
74
+
75
+ publish-pypi:
76
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
77
+ needs:
78
+ - sdist
79
+ - wheel
80
+ runs-on: ubuntu-latest
81
+ environment:
82
+ name: pypi
83
+ permissions:
84
+ id-token: write
85
+ steps:
86
+ - uses: actions/download-artifact@v8
87
+ with:
88
+ path: dist
89
+ merge-multiple: true
90
+ - uses: pypa/gh-action-pypi-publish@release/v1
91
+ with:
92
+ packages-dir: dist
93
+ skip-existing: true
imexp-0.1.0/.gitignore ADDED
@@ -0,0 +1,177 @@
1
+ # Custom Folders
2
+ /.refs
3
+ /.pylintcache
4
+ /data/logs
5
+ /data/base
6
+ /data/config/
7
+ .staging/
8
+ /src/imexp/bin/
9
+
10
+ # Custom Extensions
11
+ *.log.*
12
+
13
+ # Custom Files
14
+ .DS_Store
15
+
16
+ # Byte-compiled / optimized / DLL files
17
+ __pycache__/
18
+ *.py[cod]
19
+ *$py.class
20
+
21
+ # C extensions
22
+ *.so
23
+
24
+ # Distribution / packaging
25
+ .Python
26
+ build/
27
+ develop-eggs/
28
+ dist/
29
+ downloads/
30
+ eggs/
31
+ .eggs/
32
+ lib/
33
+ lib64/
34
+ parts/
35
+ sdist/
36
+ var/
37
+ wheels/
38
+ share/python-wheels/
39
+ *.egg-info/
40
+ .installed.cfg
41
+ *.egg
42
+ MANIFEST
43
+
44
+ # PyInstaller
45
+ # Usually these files are written by a python script from a template
46
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
47
+ *.manifest
48
+ *.spec
49
+
50
+ # Installer logs
51
+ pip-log.txt
52
+ pip-delete-this-directory.txt
53
+
54
+ # Unit test / coverage reports
55
+ htmlcov/
56
+ .tox/
57
+ .nox/
58
+ .coverage
59
+ .coverage.*
60
+ .cache
61
+ nosetests.xml
62
+ coverage.xml
63
+ *.cover
64
+ *.py,cover
65
+ .hypothesis/
66
+ .pytest_cache/
67
+ cover/
68
+
69
+ # Translations
70
+ *.mo
71
+ *.pot
72
+
73
+ # Django stuff:
74
+ *.log
75
+ local_settings.py
76
+ db.sqlite3
77
+ db.sqlite3-journal
78
+
79
+ # Flask stuff:
80
+ instance/
81
+ .webassets-cache
82
+
83
+ # Scrapy stuff:
84
+ .scrapy
85
+
86
+ # Sphinx documentation
87
+ docs/_build/
88
+
89
+ # PyBuilder
90
+ .pybuilder/
91
+ target/
92
+
93
+ # Jupyter Notebook
94
+ .ipynb_checkpoints
95
+
96
+ # IPython
97
+ profile_default/
98
+ ipython_config.py
99
+
100
+ # pyenv
101
+ # For a library or package, you might want to ignore these files since the code is
102
+ # intended to run in multiple environments; otherwise, check them in:
103
+ # .python-version
104
+
105
+ # pipenv
106
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
107
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
108
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
109
+ # install all needed dependencies.
110
+ #Pipfile.lock
111
+
112
+ # poetry
113
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
114
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
115
+ # commonly ignored for libraries.
116
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
117
+ #poetry.lock
118
+
119
+ # pdm
120
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
121
+ #pdm.lock
122
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
123
+ # in version control.
124
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
125
+ .pdm.toml
126
+ .pdm-python
127
+ .pdm-build/
128
+
129
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
130
+ __pypackages__/
131
+
132
+ # Celery stuff
133
+ celerybeat-schedule
134
+ celerybeat.pid
135
+
136
+ # SageMath parsed files
137
+ *.sage.py
138
+
139
+ # Environments
140
+ .env
141
+ .venv
142
+ env/
143
+ venv/
144
+ ENV/
145
+ env.bak/
146
+ venv.bak/
147
+
148
+ # Spyder project settings
149
+ .spyderproject
150
+ .spyproject
151
+
152
+ # Rope project settings
153
+ .ropeproject
154
+
155
+ # mkdocs documentation
156
+ /site
157
+
158
+ # mypy
159
+ .mypy_cache/
160
+ .dmypy.json
161
+ dmypy.json
162
+
163
+ # Pyre type checker
164
+ .pyre/
165
+
166
+ # pytype static type analyzer
167
+ .pytype/
168
+
169
+ # Cython debug symbols
170
+ cython_debug/
171
+
172
+ # PyCharm
173
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
174
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
175
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
176
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
177
+ #.idea/
imexp-0.1.0/AGENTS.md ADDED
@@ -0,0 +1,301 @@
1
+ # Agent Guide
2
+
3
+ Always check for a virtual environment first. Do not assume a virtual environment is activated. Use the venv when running commands.
4
+
5
+ if Unix:
6
+
7
+ - `./.venv/bin/python`
8
+ - `./.venv/bin/pip`
9
+ - `./.venv/bin/pylint`
10
+ - `./.venv/bin/pytest`
11
+
12
+ if Windows:
13
+
14
+ - `.\.venv\Scripts\python`
15
+ - `.\.venv\Scripts\pip`
16
+ - `.\.venv\Scripts\pylint`
17
+ - `.\.venv\Scripts\pytest`
18
+
19
+ Run pytest before and after each turn so you know if your work broke any logic.
20
+
21
+ Once we come to an agreement on what code should be committed craft a commit mesage for the new code based on ./docs/dev/commits.md and commit the code, make sure the body is a hyphenated bullet list. Do not push unless asked.
22
+
23
+ ## Brainstorming
24
+
25
+ Ask me clarifying questions until you know what I want to build and walk me through the setup step by step.
26
+
27
+ ## Syntax
28
+
29
+ Follow these rules when generating or modifying code in this repository:
30
+
31
+ - Use guard clauses to exit early. Keep the happy path straight and flat.
32
+ - Avoid deep nesting; split logic into small functions instead.
33
+ - Prefer multiple `if` statements over large `if/elif/else` pyramids.
34
+ - Use `else` sparingly and only when it clarifies binary logic.
35
+ - Avoid using try except blocks unless absolutely necessary.
36
+ - Catch only specific exceptions you expect and know how to handle.
37
+ - Do NOT use `except Exception:`
38
+ - If you catch broadly for logging, re-raise so errors aren’t swallowed.
39
+ - Keep functions focused on one responsibility with clear inputs/outputs.
40
+ - Fail fast when invariants are violated; use precise error messages.
41
+ - Prioritize clarity over cleverness, code should read top-to-bottom like a story.
42
+ - For CLI tools, use `Declarative` control flow style. Do NOT use exit-code style control flow. Let the entrypoint handle process termination.
43
+ - We are in the pre-alpha building stage, there is no need for fallbacks or backwards compatability. Cutover. This is unreleased, all code needs to be canonical.
44
+
45
+ (See below for full guidelines and examples.)
46
+
47
+ # AGENTS: Coding Style & Control-Flow Guidelines
48
+
49
+ This project prefers explicit, predictable control flow and narrow, intentional error handling.
50
+
51
+ The goals:
52
+
53
+ - Code that reads top-to-bottom like a story
54
+ - Minimal nesting and cognitive load
55
+ - If necessary, use exceptions and branches that reflect *specific, known* conditions
56
+
57
+ If you’re an agent writing or editing code here, follow the rules below.
58
+
59
+ ---
60
+
61
+ ## 1. Control Flow: Prefer Guard Clauses, Avoid Deep Nesting
62
+
63
+ ### 1.1. Guard clauses over `else` pyramids
64
+
65
+ Use early returns (guard clauses) for invalid inputs, edge cases, and “nothing to do” states.
66
+
67
+ ✅ Preferred:
68
+
69
+ ```python
70
+ def process_item(item):
71
+ if item is None:
72
+ return None
73
+
74
+ if not item.active:
75
+ return None
76
+
77
+ if item.value < 0:
78
+ raise ValueError("value must be non-negative")
79
+
80
+ return item.value * 2
81
+ ```
82
+
83
+ ❌ Avoid:
84
+
85
+ ```python
86
+ def process_item(item):
87
+ if item is not None:
88
+ if item.active:
89
+ if item.value >= 0:
90
+ return item.value * 2
91
+ else:
92
+ raise ValueError("value must be non-negative")
93
+ else:
94
+ return None
95
+ else:
96
+ return None
97
+ ```
98
+
99
+ Rules:
100
+
101
+ - Prefer a flat, linear “happy path”.
102
+ - Use multiple `if` guard clauses instead of a single `if/elif/else` tower when it improves clarity.
103
+ - `else:` is not banned, but avoid it when it hides which conditions actually lead there.
104
+
105
+ ### 1.2. Keep functions small and focused
106
+
107
+ If you feel tempted to add multiple `else` branches or deeply nested conditionals:
108
+
109
+ - First try splitting logic into smaller functions.
110
+ - Each function should do one clear thing and return early when preconditions fail.
111
+
112
+ ---
113
+
114
+ ## 2. Exceptions: Be Specific, Avoid Catch-Alls
115
+
116
+ ### 2.1. No `except Exception:` (almost always)
117
+
118
+ Do not use `except Exception:` or `except BaseException:` unless you are:
119
+
120
+ - Logging/cleaning up and then re-raising.
121
+
122
+ Rules:
123
+
124
+ - Catch only the specific exceptions you expect and know how to handle.
125
+ - If you don't know how to recover, don’t catch. Let it propagate.
126
+
127
+ ### 2.2. Group related exceptions explicitly
128
+
129
+ If multiple known failure modes can be handled in the same way, catch them as a tuple.
130
+
131
+ ### 2.3. Logging + re-raise instead of swallowing
132
+
133
+ If you need to observe unexpected exceptions, log and re-raise.
134
+
135
+ This is acceptable at boundaries or in “monitoring” code. The key is: don’t swallow.
136
+
137
+ ---
138
+
139
+ ## 3. Where Catch-Alls *Are* Allowed
140
+
141
+ There are a few places where a broad catch is acceptable and sometimes required.
142
+
143
+ ### 3.1. Top-level entry points
144
+
145
+ Guidelines:
146
+
147
+ - Keep the handler small (log, cleanup, exit).
148
+ - Do not bury business logic inside such a handler.
149
+
150
+ ### 3.2. Custom exception hierarchies
151
+
152
+ For libraries or complex modules, define a project-specific base exception.
153
+
154
+ ✅ Preferred:
155
+
156
+ ```python
157
+ class AppError(Exception): pass
158
+ class ConfigError(AppError): pass
159
+ class NetworkError(AppError): pass
160
+
161
+ def run_step():
162
+ raise ConfigError("Missing token")
163
+ ```
164
+
165
+ Then callers can do:
166
+
167
+ ```python
168
+ try:
169
+ run_step()
170
+ except AppError as e:
171
+ handle_expected_error(e)
172
+ ```
173
+
174
+ This avoids `except Exception:` while still providing a single, meaningful “umbrella”.
175
+
176
+ ---
177
+
178
+ ## 4. EAFP vs LBYL
179
+
180
+ Python style encourages EAFP (Easier to Ask Forgiveness than Permission) — using try/except instead of pre-checking everything — but we still want specificity.
181
+
182
+ ### 4.1. Use EAFP for specific exceptions
183
+
184
+ ### 4.2. Use LBYL sparingly for cheap, obvious checks
185
+
186
+ For very cheap validations or when the exception would be ambiguous, use LBYL.
187
+
188
+ Avoid doing heavy or redundant pre-checks that just duplicate what the operation itself will tell you via exceptions.
189
+
190
+ ---
191
+
192
+ ## 5. Conditionals: How to Use `if`, `elif`, `else`
193
+
194
+ ### 5.1. Prefer multiple `if` + early return to giant `if/elif/else`
195
+
196
+ ✅ Preferred:
197
+
198
+ ```python
199
+ def categorize(user):
200
+ if user is None:
201
+ return "anonymous"
202
+
203
+ if user.is_admin:
204
+ return "admin"
205
+
206
+ if user.is_staff:
207
+ return "staff"
208
+
209
+ return "user"
210
+ ```
211
+
212
+ ❌ Less preferred:
213
+
214
+ ```python
215
+ def categorize(user):
216
+ if user is None:
217
+ return "anonymous"
218
+ elif user.is_admin:
219
+ return "admin"
220
+ elif user.is_staff:
221
+ return "staff"
222
+ else:
223
+ return "user"
224
+ ```
225
+
226
+ Both are valid Python. The first style is preferred here because:
227
+
228
+ - Each condition stands alone.
229
+ - The “fall-through” default path is visually obvious at the end.
230
+ - You don’t need to mentally manage the full `if/elif/elif/else` ladder.
231
+
232
+ ### 5.2. When `else` *is* okay
233
+
234
+ `else` is fine when:
235
+
236
+ - There are only 1–2 branches and it aids readability.
237
+ - The logic is truly binary.
238
+
239
+ Example:
240
+
241
+ ```python
242
+ def normalize_flag(value: str) -> bool:
243
+ value = value.lower().strip()
244
+ if value in {"1", "true", "yes", "y"}:
245
+ return True
246
+ else:
247
+ return False
248
+ ```
249
+
250
+ Use judgement: prefer clarity over dogma.
251
+
252
+ ---
253
+
254
+ ## 6. Error Messages and Fail-Fast Behavior
255
+
256
+ - Fail early and loudly when invariants are broken.
257
+ - Raise exceptions with messages that explain what and why, not just that something failed.
258
+
259
+ ✅ Good:
260
+
261
+ ```python
262
+ def get_user(id: int) -> User:
263
+ if id <= 0:
264
+ raise ValueError(f"id must be positive, got {id}")
265
+ ...
266
+ ```
267
+
268
+ ---
269
+
270
+ ## 7. Linting, Pylint, and Broad Exception Rules
271
+
272
+ We care about linters (e.g. Pylint) and generally do not want to disable rules globally.
273
+
274
+ - Do not disable warnings
275
+
276
+ Example:
277
+
278
+ ```python
279
+ def worker_loop():
280
+ while True:
281
+ try:
282
+ process_one_job()
283
+ except Exception: # pylint: disable=broad-exception-caught
284
+ log_exception_and_continue()
285
+ ```
286
+
287
+ Use this pattern only where absolutely necessary and structurally justified.
288
+
289
+ ---
290
+
291
+ ## 8. Summary for Agents
292
+
293
+ When generating or editing code in this repository:
294
+
295
+ 1. Use guard clauses and early returns.
296
+ 2. Keep control flow flat when possible. Avoid deep nesting and giant `if/elif/else` ladders.
297
+ 3. Catch specific exceptions, not `Exception`
298
+ 4. Don’t swallow exceptions silently.
299
+ 5. Favor small, focused functions that reflect one clear responsibility.
300
+
301
+ If a simpler structure conflicts with these rules, prioritize clarity and explicitness over cleverness.