Metadata-Version: 2.4
Name: emzed-spyder
Version: 3.0.3
Summary: emzed IDE, see also https://emzed.ethz.ch
Author-email: Uwe Schmitt <uwe.schmitt@id.ethz.ch>
License: MIT
Project-URL: Homepage, https://emzed.ethz.ch
Requires-Python: <3.15,>=3.11
Description-Content-Type: text/markdown
Requires-Dist: alabaster==1.0.0
Requires-Dist: arrow==1.3.0
Requires-Dist: astroid==3.3.11
Requires-Dist: asttokens==3.0.0
Requires-Dist: atomicwrites==1.4.1
Requires-Dist: attrs==25.3.0
Requires-Dist: autopep8==2.0.4
Requires-Dist: babel==2.17.0
Requires-Dist: bcrypt==4.3.0
Requires-Dist: beautifulsoup4==4.13.4
Requires-Dist: binaryornot==0.4.4
Requires-Dist: black==25.1.0
Requires-Dist: bleach[css]==6.2.0
Requires-Dist: certifi==2025.8.3
Requires-Dist: cffi==1.17.1
Requires-Dist: chardet==3.0.4
Requires-Dist: charset-normalizer==3.4.3
Requires-Dist: click==8.2.1
Requires-Dist: cloudpickle==3.1.1
Requires-Dist: comm==0.2.3
Requires-Dist: cookiecutter==2.6.0
Requires-Dist: cryptography==45.0.6
Requires-Dist: debugpy==1.8.16
Requires-Dist: decorator==5.2.1
Requires-Dist: defusedxml==0.7.1
Requires-Dist: diff-match-patch==20241021
Requires-Dist: dill==0.4.0
Requires-Dist: docstring-to-markdown==0.17
Requires-Dist: docutils==0.21.2
Requires-Dist: executing==2.2.0
Requires-Dist: fastjsonschema==2.21.1
Requires-Dist: flake8==7.1.2
Requires-Dist: idna==3.10
Requires-Dist: imagesize==1.4.1
Requires-Dist: importlib-metadata==8.7.0
Requires-Dist: inflection==0.5.1
Requires-Dist: intervaltree==3.1.0
Requires-Dist: ipykernel==6.30.1
Requires-Dist: ipython==8.37.0
Requires-Dist: isort==6.0.1
Requires-Dist: jaraco-classes==3.4.0
Requires-Dist: jaraco-context==6.0.1
Requires-Dist: jaraco-functools==4.2.1
Requires-Dist: jedi==0.19.2
Requires-Dist: jinja2==3.1.6
Requires-Dist: jsonschema==4.25.0
Requires-Dist: jsonschema-specifications==2025.4.1
Requires-Dist: jupyter-client==8.6.3
Requires-Dist: jupyter-core==5.8.1
Requires-Dist: jupyterlab-pygments==0.3.0
Requires-Dist: keyring==25.6.0
Requires-Dist: markdown-it-py==3.0.0
Requires-Dist: markupsafe==3.0.2
Requires-Dist: matplotlib-inline==0.1.7
Requires-Dist: mccabe==0.7.0
Requires-Dist: mdurl==0.1.2
Requires-Dist: mistune==3.1.3
Requires-Dist: more-itertools==10.7.0
Requires-Dist: mypy-extensions==1.1.0
Requires-Dist: nbclient==0.10.2
Requires-Dist: nbconvert==7.16.6
Requires-Dist: nbformat==5.10.4
Requires-Dist: nest-asyncio==1.6.0
Requires-Dist: numpy>=2
Requires-Dist: numpydoc==1.9.0
Requires-Dist: packaging==25.0
Requires-Dist: pandas
Requires-Dist: pandocfilters==1.5.1
Requires-Dist: parso==0.8.4
Requires-Dist: pathspec==0.12.1
Requires-Dist: pexpect==4.9.0
Requires-Dist: pickleshare==0.7.5
Requires-Dist: platformdirs==4.3.8
Requires-Dist: pluggy==1.6.0
Requires-Dist: prompt-toolkit==3.0.51
Requires-Dist: psutil==7.0.0
Requires-Dist: ptyprocess==0.7.0
Requires-Dist: pure-eval==0.2.3
Requires-Dist: pycodestyle==2.12.1
Requires-Dist: pycparser==2.22
Requires-Dist: pydocstyle==6.3.0
Requires-Dist: pyflakes==3.2.0
Requires-Dist: pygments==2.19.2
Requires-Dist: pylint==3.3.8
Requires-Dist: pylint-venv==3.0.4
Requires-Dist: pyls-spyder==0.4.0
Requires-Dist: pyqt5~=5.15.0
Requires-Dist: pyqt5-qt5; sys_platform != "win32"
Requires-Dist: pyqt5-sip
Requires-Dist: pyqtwebengine
Requires-Dist: pyqtwebengine-qt5; sys_platform != "win32"
Requires-Dist: python-dateutil==2.9.0.post0
Requires-Dist: python-jsonrpc-server==0.4.0
Requires-Dist: python-lsp-black==2.0.0
Requires-Dist: python-lsp-jsonrpc==1.1.2
Requires-Dist: python-lsp-server[all]==1.12.2
Requires-Dist: python-slugify==8.0.4
Requires-Dist: pytoolconfig[global]==1.3.1
Requires-Dist: pytz==2025.2
Requires-Dist: pyyaml==6.0.2
Requires-Dist: pyzmq==27.0.1
Requires-Dist: guidata
Requires-Dist: qdarkstyle==3.2.3
Requires-Dist: qstylizer==0.2.4
Requires-Dist: qtawesome==1.3.1
Requires-Dist: qtconsole==5.5.2
Requires-Dist: qtpy==2.4.3
Requires-Dist: referencing==0.36.2
Requires-Dist: requests==2.32.4
Requires-Dist: rich==14.1.0
Requires-Dist: roman-numerals-py==3.1.0
Requires-Dist: rope==1.14.0
Requires-Dist: rpds-py==0.27.0
Requires-Dist: rtree==1.4.0
Requires-Dist: setproctitle==1.3.6
Requires-Dist: six==1.17.0
Requires-Dist: snowballstemmer==3.0.1
Requires-Dist: sortedcontainers==2.4.0
Requires-Dist: soupsieve==2.7
Requires-Dist: sphinx
Requires-Dist: sphinxcontrib-applehelp
Requires-Dist: sphinxcontrib-devhelp
Requires-Dist: sphinxcontrib-htmlhelp
Requires-Dist: sphinxcontrib-jsmath
Requires-Dist: sphinxcontrib-qthelp
Requires-Dist: sphinxcontrib-serializinghtml
Requires-Dist: spyder==5.5.6
Requires-Dist: stack-data==0.6.3
Requires-Dist: text-unidecode==1.3
Requires-Dist: textdistance==4.6.3
Requires-Dist: three-merge==0.1.1
Requires-Dist: tinycss2==1.4.0
Requires-Dist: tomlkit==0.13.3
Requires-Dist: tornado==6.5.2
Requires-Dist: traitlets==5.14.3
Requires-Dist: types-python-dateutil==2.9.0.20250809
Requires-Dist: typing-extensions==4.14.1
Requires-Dist: tzdata==2025.2
Requires-Dist: ujson==5.10.0
Requires-Dist: urllib3==2.5.0
Requires-Dist: watchdog==6.0.0
Requires-Dist: wcwidth==0.2.13
Requires-Dist: webencodings==0.5.1
Requires-Dist: whatthepatch==1.0.7
Requires-Dist: wurlitzer==3.1.1
Requires-Dist: yapf==0.43.0
Requires-Dist: zipp==3.23.0

# emzed.spyder

`emzed.spyder` is a customized distribution of the [Spyder IDE](https://www.spyder-ide.org/)
for the [emzed](https://emzed.ethz.ch) mass spectrometry platform (ETH Zurich,
Scientific IT Services).

## emzed Related Functions in the Spyder Terminal

`remote_interpreter_startup.py` is injected as the IPython kernel startup
file. Because it runs as a startup script, everything defined at its top level
lands directly in the user namespace — including `emzed` itself (preloaded) and
all `emzed_*` helper functions listed below:

| Function | Description |
|---|---|
| `emzed_help` | list all emzed specific commands |
| `emzed_edit_config` | reopen the first-run configuration dialog |
| `emzed_update` | Update `emzed` and `emzed_gui` |
| `emzed_check_versions` | Check installed vs. latest versions |
| `emzed_install_ext` | Install an emzed extension |


To separate projects with separated virtual environments
or to create `emzed` extensions:

| Function | Description |
|---|---|
| `emzed_project_new` | Create a new project |
| `emzed_project_activate` | Switch the active project |
| `emzed_project_deactivate` | Deactivate the current project |
| `emzed_project_list` | List all projects |
| `emzed_project_update` | Reinstall project after `setup.py` changes |
| `emzed_project_test` | Run the project's test suite |
| `emzed_project_test_coverage` | Run tests and open coverage report |
| `emzed_project_build_wheel` | Build a distributable wheel |
| `emzed_project_upload` / `emzed_project_upload_test` | Upload to PyPI |

Run `emzed_help()` in the console for a full listing.

### Project locations on disk

Projects are stored in a platform-specific folder:

- **macOS/Linux**: `~/.emzed3_projects/<project_name>/`
- **Windows**: `%APPDATA%\emzed3_projects\<project_name>\`

Each project folder contains a `.venv/` subdirectory with the project's own
Python environment.

### Project workflow

`emzed_project_new(name)` scaffolds a new project using a cookiecutter
template. It:

1. Creates the project folder under the projects directory above.
2. Generates a `setup.py`, `requirements_dev.txt`, `LICENSE.txt`, and a
   `src/emzed_ext_<name>/` package skeleton.
3. Creates a `.venv/` that inherits `emzed` and its dependencies from the
   shared remote venv (via a `.pth` file), so only project-specific packages
   need to be installed on top.
4. Activates the new project and opens `setup.py` in the editor.

After editing `setup.py` and `requirements_dev.txt`, run
`emzed_project_update()` to install the project and its dependencies into the
project venv.

`emzed_project_activate(name)` switches the active project — the next IPython
console that opens will use that project's `.venv` as its Python interpreter
and start in the project folder. Only one project can be active at a time;
`emzed_project_deactivate()` returns to the default remote venv.


## Variable Explorer Integration

`emzed_spyder_kernels.py` runs inside the remote kernel and patches
`spyder_kernels.utils.nsview` so that emzed-specific types display correctly
in Spyder's variable explorer:

| Patch | Effect |
|---|---|
| `get_type_string` | Shows `"Table"`, `"PeakMap"`, or `"ImmutablePeakMap"` as the type |
| `get_size` | Shows meaningful size (e.g. `"42 row x 5 columns"` for a Table) |
| `value_to_display` | Shows column names for `Table` objects |
| `is_supported` | Marks `Table`/`ImmutablePeakMap` as not directly serialisable |
| `globalsfilter` | Hides closed `Table`/`ImmutablePeakMap` objects from the explorer |

These patches must live in the kernel process (not Spyder's process) because
the variable explorer inspects live objects — but `isinstance` checks cannot
be used from the Spyder side, since `emzed` is only installed in the remote
venv. Instead, `patch_gui.py` detects `Table`/`PeakMap` by the type name
string returned from the kernel when a double-click occurs, then calls
`_inspect()` directly in the remote shell to open the viewer without copying
data across the process boundary.

## Architecture

The project is a thin wrapper around Spyder that patches it at startup time.
It does not fork Spyder — it installs alongside it and monkey-patches live
classes before Spyder finishes booting.

**Entry point**: `emzed_spyder.start:main` imports `patch_all`, then delegates
to `spyder.app.start.main`.

### Patch modules (applied in strict order)

| Module | What it patches |
|---|---|
| `patch_config.py` | Config folder location, splash/icon assets, theme, update checks, startup scripts |
| `patch_gui.py` | Window title, variable explorer double-click, code completion filtering, IPython prompt, console working directory |
| `patch_remote_kernel_startup.py` | Kernel spec (argv, env), console banner with ASCII art, active project/runtime status, and version check |

The ordering is critical: `set_remote_python_interpreter` must run after all
config patches because Spyder caches some config at import time.

## Two-Venv Model

`emzed.spyder` uses two separate Python environments:

- **Spyder venv** — runs the IDE itself (PyQt5, Spyder, `emzed_spyder`)
- **Remote venv** (`~/.emzed3/remote_venv/`) — runs user code; contains
  `emzed`, `emzed_gui`, `emzed_spyder_kernels`

At startup, the remote venv is validated (required packages are imported to
confirm they work) and recreated from scratch if invalid, downloading setup
files from GitLab.

User projects each get their own `.venv` that inherits from the remote venv
via a `.pth` file, avoiding re-installation of the heavy `emzed` stack.

## CI/CD and Tooling

- **CI**: GitLab CI (`.gitlab-ci.yml`)
- **Linting**: ruff, black, flake8 via pre-commit hooks
- **Dependency management**: `uv`
- **Platform notes**: PyQt5 is pinned on Windows; `uv` environments are scoped
  to Linux and Windows (macOS uses system Qt)
