Skip to content

Commit 170f535

Browse files
authored
Merge pull request #8 from sodascience/9424
9424
2 parents 1378a87 + 25bcf8d commit 170f535

10 files changed

Lines changed: 530 additions & 549 deletions

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
.venv
1+
.venv
2+
.DS_Store

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.12

README.md

Lines changed: 88 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,106 +1,133 @@
1-
## Configuring Python in CBS Remote Access (RA)
1+
## What this is for
22

3-
**Python is not installed by default at CBS RA (yet).** To activate Python, contact the CBS microdata team at [`microdata@cbs.nl`](mailto:microdata@cbs.nl).
3+
CBS Remote Access (RA) only lets you use Python packages that CBS has approved and installed in advance. To get a package approved, you submit a file pip requirements.txt file listing exactly which packages (and versions) you need, and CBS installs them for you.
44

5-
### Default Python Packages
5+
This repository helps you build that file correctly, without needing to understand Python packaging in depth. You will:
66

7-
By default, some packages are available in Python at CBS RA, such as `pandas`, `pyreadstat` or `matplotlib`
7+
0. Install a tool called `uv` that does the hard work for you.
8+
1. Download this repository to your computer.
9+
2. Write down which packages you want, in a simple text file (`requirements.in`).
10+
3. Run one command that turns that list into a ready-to-send file (`environment0000.txt`).
11+
4. Test that file on your own computer before sending it (optional, but recommended).
12+
5. Email the file to CBS.
13+
6. Optionally, set up the same packages on your own computer so you can work locally too.
814

9-
If you require additional packages or specific versions, follow the steps below to create and submit your own Python environment.
15+
You only need to follow these steps once per project (and again whenever you want to add or remove a package).
1016

1117
---
1218

13-
### Creating a Custom Python Environment
19+
## Basic configuration
1420

15-
Follow these instructions to set up and submit a customized Python environment. You need to use a **Windows** computer.
21+
### Step 0: Install `uv`
1622

17-
#### Step 1: Check Existing Environment
23+
`uv` is the tool that does the heavy lifting (figuring out compatible package
24+
versions). Install it once, following the official instructions:
25+
https://docs.astral.sh/uv/getting-started/installation/
1826

19-
- Check if `environment0000.txt` (replace `0000` with your actual project number) already contains the required packages and suitable versions.
20-
- **If yes:** Send this file directly to CBS.
21-
- **If no:** Continue to Step 2.
27+
If you've never used a terminal before: a terminal is just a window where you
28+
type commands instead of clicking buttons. On Windows, open "PowerShell" or
29+
"Command Prompt"; on Mac, open "Terminal" (both are pre-installed). The
30+
installation instructions above include a single command to paste in and run.
2231

23-
#### Step 2: Create the Environment (Windows + Conda)
32+
### Step 1: Download this repository to your computer
2433

25-
Install conda locally (only needed if you do not already have Conda installed):
26-
- Follow the official Conda installation instructions [here](https://conda.io/projects/conda/en/latest/user-guide/install/index.html#regular-installation).
27-
- If you're unfamiliar with command-line tools, consider installing [Anaconda](https://www.anaconda.com/products/individual) instead.
28-
29-
On your local Windows machine:
34+
If you're comfortable with git:
3035

3136
```sh
32-
conda create -n 0000 python
33-
conda activate 0000
34-
conda install pip
35-
pip install package_name
37+
git clone <repo-url>
38+
cd cbs_python
3639
```
3740

38-
Replace `package_name` with the packages you need (e.g., `pip install numpy`). If you want to install all the packages in the requirements.txt file in this repository, use `pip install -r requirements.txt`
41+
Otherwise, download the repository as a ZIP from its webpage and unzip it into a
42+
local folder.
3943

40-
**Note:** If using Jupyter Notebook or Spyder, install these explicitly, e.g.:
44+
### Step 2: Say which packages you want
4145

42-
```sh
43-
pip install jupyter spyder
44-
```
46+
Open `requirements.in` in a text editor. It's a plain list of package names, one
47+
per line, already organized into groups (data handling, visualization, etc.), with
48+
a short comment next to each one explaining what it's for.
4549

46-
#### Step 3: Export the Environment
50+
- To add a package, add a new line with its name.
51+
- To remove one, delete its line (or put a `#` in front of it to keep it for later).
4752

48-
Export the environment into a requirements file:
53+
You don't need to write version numbers — the next step figures those out for you.
4954

50-
```sh
51-
pip freeze > C:\temp\environment0000.txt
52-
```
55+
### Step 3: Let `uv` work out the exact versions
5356

54-
Check `environment0000.txt` for local paths (`file://`). If found, regenerate using:
57+
CBS RA runs Windows, so generate a Windows-specific version of the package list. Open a terminal in the repository folder and run:
5558

5659
```sh
57-
pip list --format=freeze > C:\temp\environment0000.txt
60+
uv pip compile requirements.in --python-version 3.12 --python-platform windows --no-annotate --no-header --emit-find-links -o environment0000.txt
5861
```
5962

60-
#### Step 4: Verify Environment
63+
`--emit-find-links` is needed because some packages (e.g. the PyTorch Geometric
64+
ones) aren't on the normal package index — `requirements.in` points `pip` at
65+
an extra URL for those via a `--find-links` line, and this flag copies that
66+
line into `environment0000.txt` so the file still works when installed on its
67+
own with plain `pip`, without `requirements.in` alongside it.
6168

62-
Validate your environment by removing and recreating it:
69+
Rename `environment0000.txt` so `0000` matches your project number. This file can
70+
be installed with plain `pip` (no `uv` needed), which is what CBS RA will do.
71+
72+
### Step 4: Test that it works in your own Windows computer [optional, highly recommended]
73+
74+
Before sending the file to CBS, test it in a fresh, empty environment (a
75+
"venv"). This catches problems early, while you can still fix them. The
76+
easiest way is with `uv`, which creates the venv and installs into it without
77+
needing to activate anything:
6378

6479
```sh
65-
conda remove -n 0000 --all
66-
conda create -n 0000
67-
conda activate 0000
68-
conda install pip
69-
pip install -r C:\temp\environment0000.txt
80+
uv venv ~/.venvs/cbs-test
81+
uv pip install -r environment0000.txt --python ~/.venvs/cbs-test
7082
```
7183

72-
Test thoroughly before submission by running python and importing your packages one by one.
73-
74-
#### Step 5: Submit Your Environment
84+
If this finishes without errors, the file is ready to send. You can also run
85+
`check_environment.py`, which goes through every package in `requirements.in`
86+
and checks that it actually imports and reports a version (not just that it
87+
installed):
7588

76-
Send your verified `environment0000.txt` (replace 0000 by your project number) to CBS via email.
89+
```sh
90+
uv run --python ~/.venvs/cbs-test python check_environment.py
91+
```
7792

93+
When you're done testing, delete the test environment:
7894

95+
```sh
96+
rm -rf ~/.venvs/cbs-test
97+
```
7998

80-
---
99+
(If you want to test with plain `pip` instead — closer to exactly what CBS
100+
will run — replace the two `uv` commands above with `python -m venv ~/.venvs/cbs-test`,
101+
then activate it with `~/.venvs/cbs-test/Scripts/activate` on Windows or
102+
`source ~/.venvs/cbs-test/bin/activate` on Mac/Linux, and run
103+
`pip install -r environment0000.txt`.)
81104

82-
## Using Python at CBS RA
105+
### Step 5: Email environmnet000.txt to CBS
83106

84-
We recommend to use Python through Visual Studio Code (VS Code), installed by default:
107+
Email it to CBS and ask them to activate Python in your project and install the environment.
85108

86-
- In VS Code, select the Python interpreter in the bottom-right corner of the editor.
109+
### Step 6 [optional]: Work on your own non-Windows computer
87110

88-
You could also use Python through Jupyter in RA, for that, open an Anaconda terminal in the RA and run:
111+
The environment created in Step 3 is locked to Windows
112+
(`--python-platform windows`), so it won't install on macOS or Linux. To work
113+
locally on a non-Windows computer, compile a second file for your own
114+
platform — just drop `--python-platform windows` so `uv` resolves wheels for
115+
whatever computer you're actually running on:
89116

90117
```sh
91-
conda activate 0000
92-
jupyter notebook --notebook-dir=H:
118+
uv pip compile requirements.in --python-version 3.12 --no-annotate --no-header --emit-find-links -o environment_local.txt
119+
uv venv ~/.venvs/cbs-python
120+
uv pip install -r environment_local.txt --python ~/.venvs/cbs-python
93121
```
94122

95-
This opens Jupyter in your shared directory (`H:`).
96-
97-
---
123+
This creates a real environment (not a throwaway test one like Step 4), so
124+
you can use it for actual work:
98125

99-
## Contact
100-
101-
This documentation is maintained by the [ODISSEI Social Data Science (SoDa)](https://odissei-data.nl/nl/soda/) team.
102-
103-
For technical questions or suggestions:
126+
```sh
127+
uv run --python ~/.venvs/cbs-python jupyterlab
128+
```
104129

105-
- File an issue in the project's issue tracker, or
106-
- Contact [Javier Garcia-Bernardo](https://github.com/jgarciab).
130+
`uv run <command>` runs any command (Jupyter, a script, etc.) using that
131+
environment, installing anything missing automatically. You can also run
132+
`uv run --python ~/.venvs/cbs-python python check_environment.py` here to
133+
double-check everything imports correctly on your own machine.

check_environment.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"""Smoke test: for every package in requirements.in, check it imports and report its version.
2+
3+
Run with: uv run python check_environment.py
4+
(or: python check_environment.py, inside the venv you're testing)
5+
"""
6+
7+
import re
8+
import subprocess
9+
import sys
10+
from importlib.metadata import PackageNotFoundError, version
11+
12+
# pip name -> import name, only where they differ
13+
IMPORT_NAME = {
14+
"pyyaml": "yaml",
15+
"scikit-learn": "sklearn",
16+
"duckdb-engine": "duckdb_engine",
17+
"pre-commit": "pre_commit",
18+
"ipython": "IPython",
19+
"hydra-core": "hydra",
20+
"umap-learn": "umap",
21+
"metasyn-disclosure": "metasyncontrib.disclosure",
22+
"jupysql": "sql",
23+
"ibis-framework": "ibis",
24+
}
25+
# packages with no importable module (CLI tools) - version-check only
26+
NOT_IMPORTABLE = {"ruff"}
27+
28+
29+
def package_names(path: str = "requirements.in") -> list[str]:
30+
names = []
31+
for line in open(path):
32+
line = line.split("#", 1)[0].strip()
33+
if not line or line.startswith("--"):
34+
continue
35+
name = re.split(r"[;<>=\[\s]", line, 1)[0]
36+
names.append(name)
37+
return names
38+
39+
40+
def main() -> int:
41+
failed = []
42+
for pip_name in package_names():
43+
key = pip_name.lower()
44+
try:
45+
v = version(pip_name)
46+
except PackageNotFoundError:
47+
print(f"FAIL {pip_name:25} not installed")
48+
failed.append(pip_name)
49+
continue
50+
51+
if key in NOT_IMPORTABLE:
52+
print(f"OK {pip_name:25} {v} (not importable, skipped import check)")
53+
continue
54+
55+
import_name = IMPORT_NAME.get(key, key.replace("-", "_"))
56+
# run each import in its own subprocess: a crashing native extension
57+
# (e.g. an ABI mismatch) would otherwise take the whole script down
58+
result = subprocess.run(
59+
[sys.executable, "-c", f"import {import_name}"], capture_output=True, text=True
60+
)
61+
if result.returncode == 0:
62+
print(f"OK {pip_name:25} {v}")
63+
else:
64+
err = result.stderr.strip().splitlines()[-1] if result.stderr else f"exit code {result.returncode}"
65+
print(f"FAIL {pip_name:25} {v} import error -> {err}")
66+
failed.append(pip_name)
67+
68+
print()
69+
if failed:
70+
print(f"FAILED: {len(failed)} package(s) -> {', '.join(failed)}")
71+
print(
72+
"Note: a 'ModuleNotFoundError' above may just mean this script guessed the "
73+
"wrong import name, not that the package is broken. If you hit one, find the "
74+
"real import name (e.g. check the package's docs) and add it to IMPORT_NAME."
75+
)
76+
return 1
77+
print("PASS: all packages imported and reported a version.")
78+
return 0
79+
80+
81+
if __name__ == "__main__":
82+
sys.exit(main())

0 commit comments

Comments
 (0)