julialang 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.
- julialang-0.1.0/LICENSE +21 -0
- julialang-0.1.0/PKG-INFO +336 -0
- julialang-0.1.0/README.md +285 -0
- julialang-0.1.0/julialang/__init__.py +15 -0
- julialang-0.1.0/julialang/__main__.py +3 -0
- julialang-0.1.0/julialang/_state.py +241 -0
- julialang-0.1.0/julialang/cli.py +250 -0
- julialang-0.1.0/julialang/julia_cli.py +35 -0
- julialang-0.1.0/julialang/manager.py +217 -0
- julialang-0.1.0/julialang.egg-info/PKG-INFO +336 -0
- julialang-0.1.0/julialang.egg-info/SOURCES.txt +17 -0
- julialang-0.1.0/julialang.egg-info/dependency_links.txt +1 -0
- julialang-0.1.0/julialang.egg-info/entry_points.txt +3 -0
- julialang-0.1.0/julialang.egg-info/top_level.txt +1 -0
- julialang-0.1.0/pyproject.toml +55 -0
- julialang-0.1.0/setup.cfg +4 -0
- julialang-0.1.0/tests/test_crawl.py +166 -0
- julialang-0.1.0/tests/test_e2e.py +256 -0
- julialang-0.1.0/tests/test_specs_state.py +133 -0
julialang-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mohammad Raziei
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
julialang-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: julialang
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Install and switch Julia versions from pip: a tiny wheel plus a GitHub-mirrored copy of the official binaries.
|
|
5
|
+
Author-email: Mohammad Raziei <mohammadraziei1375@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Mohammad Raziei
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Source, https://github.com/mohammadraziei/julialang
|
|
29
|
+
Project-URL: Issues, https://github.com/mohammadraziei/julialang/issues
|
|
30
|
+
Project-URL: Changelog, https://github.com/mohammadraziei/julialang/releases
|
|
31
|
+
Keywords: julia,julialang,julia-lang,binary,installer,wheel,version-manager
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Intended Audience :: Developers
|
|
34
|
+
Classifier: Intended Audience :: Science/Research
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
37
|
+
Classifier: Operating System :: MacOS
|
|
38
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
39
|
+
Classifier: Programming Language :: Python :: 3
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
45
|
+
Classifier: Topic :: Software Development :: Interpreters
|
|
46
|
+
Classifier: Topic :: Scientific/Engineering
|
|
47
|
+
Requires-Python: >=3.9
|
|
48
|
+
Description-Content-Type: text/markdown
|
|
49
|
+
License-File: LICENSE
|
|
50
|
+
Dynamic: license-file
|
|
51
|
+
|
|
52
|
+
<div align="center">
|
|
53
|
+
|
|
54
|
+
# julialang
|
|
55
|
+
|
|
56
|
+
**Install, switch and remove [Julia](https://julialang.org) versions with `pip`: no installer, no `apt`, no `juliaup`.**
|
|
57
|
+
|
|
58
|
+
[](https://pypi.org/project/julialang/)
|
|
59
|
+
[](https://pypi.org/project/julialang/)
|
|
60
|
+
[](https://github.com/MohammadRaziei/julialang/actions/workflows/wheel.yml)
|
|
61
|
+
[](https://github.com/MohammadRaziei/julialang/actions/workflows/sync.yml)
|
|
62
|
+
[](LICENSE)
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install julialang && julialang install && julia
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Contents
|
|
73
|
+
|
|
74
|
+
- [Why julialang?](#why-julialang)
|
|
75
|
+
- [Quick start](#quick-start)
|
|
76
|
+
- [Everyday use](#everyday-use)
|
|
77
|
+
- [Install Julia](#install-julia) · [Run Julia](#run-julia) · [See what you have](#see-what-you-have) · [Switch version](#switch-version) · [Upgrade and downgrade](#upgrade-and-downgrade) · [Remove versions](#remove-versions) · [Remove everything](#remove-everything)
|
|
78
|
+
- [Version specs](#version-specs)
|
|
79
|
+
- [Command reference](#command-reference)
|
|
80
|
+
- [Environment variables](#environment-variables)
|
|
81
|
+
- [How it works](#how-it-works)
|
|
82
|
+
- [For maintainers](#for-maintainers)
|
|
83
|
+
- [Notes](#notes)
|
|
84
|
+
|
|
85
|
+
## Why julialang?
|
|
86
|
+
|
|
87
|
+
Like [`ziglang`](https://pypi.org/project/ziglang/) does for Zig, `julialang` makes Julia installable from Python
|
|
88
|
+
tooling.
|
|
89
|
+
|
|
90
|
+
- **Tiny wheel.** `pip install julialang` downloads a pure-Python wheel of a few KB (`py3-none-any`), the same on every platform.
|
|
91
|
+
- **No julialang.org needed.** The official binaries are mirrored on GitHub by CI; `julialang` downloads from there and verifies sha256 and md5.
|
|
92
|
+
- **Many versions side by side.** Install as many as you like, switch with one command, upgrade, downgrade, remove.
|
|
93
|
+
- **pip-style version specs.** `julialang install ">=1.10,<1.13"`, `julialang install 1.12`, `julialang install ~=1.12.2`.
|
|
94
|
+
- **Every stable release.** The mirror covers all stable Julia versions, from 0.x to the latest.
|
|
95
|
+
- **Zero dependencies.** Standard library only. Works on Linux (x86_64, aarch64, i686), macOS (x86_64, arm64) and Windows (x86_64, i686) with Python 3.9+.
|
|
96
|
+
|
|
97
|
+
## Quick start
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pip install julialang
|
|
101
|
+
julialang install # latest Julia, becomes the default
|
|
102
|
+
julia -e 'println("hello from Julia ", VERSION)'
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Julia itself is **not** inside the wheel: `julialang install` downloads it into `~/.julialang`.
|
|
106
|
+
|
|
107
|
+
## Everyday use
|
|
108
|
+
|
|
109
|
+
### Install Julia
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
julialang install # the latest version
|
|
113
|
+
julialang install 1.12 # the newest 1.12.x
|
|
114
|
+
julialang install 1.10.5 # exactly this version
|
|
115
|
+
julialang install ">=1.10,<1.13" # the newest version that satisfies the spec
|
|
116
|
+
julialang install 1.10 1.12 # several at once
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`install` only **adds** versions: nothing is removed, and the installed version becomes the default. Pass
|
|
120
|
+
`--no-default` to keep the current default:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
julialang install 1.10 --no-default
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Run Julia
|
|
127
|
+
|
|
128
|
+
`julia` is a thin shim that runs the selected Julia with your arguments:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
julia # REPL
|
|
132
|
+
julia script.jl # run a script
|
|
133
|
+
julia -e 'print(VERSION)'
|
|
134
|
+
JULIALANG_VERSION=1.10 julia -e 'print(VERSION)' # one command with another installed version
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Which Julia runs: `JULIALANG_VERSION` (a spec, must be installed), else the default chosen by `julialang`, else the
|
|
138
|
+
newest installed one. The shim never touches the network. If nothing is installed yet it tells you to run
|
|
139
|
+
`julialang install` (set `JULIALANG_AUTO_INSTALL=1` to let `julia` install the latest on first use instead).
|
|
140
|
+
|
|
141
|
+
### See what you have
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
julialang # status: installed versions, the default, the latest on the mirror
|
|
145
|
+
julialang list # installed versions and the ones the mirror offers
|
|
146
|
+
julialang list --installed # only what is on your machine
|
|
147
|
+
julialang list --all # everything the mirror has
|
|
148
|
+
julialang latest # newest version on the mirror
|
|
149
|
+
julialang which # path of the executable `julia` runs
|
|
150
|
+
julialang fetch # refresh the cached list of mirrored versions now
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Switch version
|
|
154
|
+
|
|
155
|
+
Switch the default among the versions you have installed:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
julialang use 1.10
|
|
159
|
+
julia -e 'print(VERSION)' # 1.10.x
|
|
160
|
+
julialang use latest
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`use` never downloads: if the version is not installed it tells you to `julialang install` it first.
|
|
164
|
+
|
|
165
|
+
### Upgrade and downgrade
|
|
166
|
+
|
|
167
|
+
`install` keeps old versions; `upgrade` and `downgrade` **replace** the current default:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
julialang upgrade # install the latest, make it the default, remove the previous default
|
|
171
|
+
julialang upgrade 1.12 # ... or the newest 1.12.x
|
|
172
|
+
julialang downgrade 1.10 # same, towards an older version (the spec is required)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The previous default is deleted only after the new version is downloaded and its checksum verified. Asking `upgrade`
|
|
176
|
+
for an older version (or `downgrade` for a newer one) is an error that points you to the other command; asking for
|
|
177
|
+
the version you already use just says so.
|
|
178
|
+
|
|
179
|
+
### Remove versions
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
julialang uninstall 1.12.2 # one version
|
|
183
|
+
julialang uninstall 1.10 1.11 # several
|
|
184
|
+
julialang uninstall "<1.12" --yes # a spec that matches many versions asks first; --yes skips the question
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Uninstalling the default promotes the newest remaining version to be the new default.
|
|
188
|
+
|
|
189
|
+
### Remove everything
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
rm -rf ~/.julialang # all installed Julia versions and the cache (Windows: delete %USERPROFILE%\.julialang)
|
|
193
|
+
pip uninstall julialang
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Installed versions live outside your virtual environment, so `pip uninstall julialang` alone leaves them in place.
|
|
197
|
+
|
|
198
|
+
## Version specs
|
|
199
|
+
|
|
200
|
+
Specs work like pip's, with npm-style handling of partial versions:
|
|
201
|
+
|
|
202
|
+
| Spec | Meaning |
|
|
203
|
+
| --- | --- |
|
|
204
|
+
| `latest` (or nothing) | the newest version |
|
|
205
|
+
| `1`, `1.12`, `1.12.*`, `==1.12` | the newest 1.x / 1.12.x |
|
|
206
|
+
| `1.12.4`, `==1.12.4` | exactly that version |
|
|
207
|
+
| `!=1.12.4` | anything but 1.12.4 |
|
|
208
|
+
| `>=1.10`, `<1.13` | comparison, as in pip |
|
|
209
|
+
| `<=1.12`, `>1.12` | with a partial version the whole line counts: `<=1.12` includes 1.12.x, `>1.12` starts at 1.13 |
|
|
210
|
+
| `~=1.12.2` | compatible release: `>=1.12.2,<1.13` (and `~=1.12` is `>=1.12,<2`) |
|
|
211
|
+
| `>=1.10,<1.13` | comma-joined: every part must hold, the newest match wins |
|
|
212
|
+
|
|
213
|
+
Quote specs that contain `<` or `>` so the shell does not treat them as redirection. Several words are joined with
|
|
214
|
+
commas, so `julialang install ">=1.10" "<1.13"` means the same as `julialang install ">=1.10,<1.13"`.
|
|
215
|
+
|
|
216
|
+
## Command reference
|
|
217
|
+
|
|
218
|
+
| Command | What it does |
|
|
219
|
+
| --- | --- |
|
|
220
|
+
| `julialang` / `julialang status` | installed versions, the default and the latest available |
|
|
221
|
+
| `julialang list [--all\|--installed]` | installed and mirrored versions |
|
|
222
|
+
| `julialang fetch` | refresh the cached list of mirrored versions |
|
|
223
|
+
| `julialang latest` | print the newest mirrored version |
|
|
224
|
+
| `julialang install [SPEC...] [--no-default]` | download and install (latest if no spec); makes it the default |
|
|
225
|
+
| `julialang uninstall SPEC... [--yes]` | delete versions (aliases: `remove`, `rm`) |
|
|
226
|
+
| `julialang use SPEC` | set the default among installed versions |
|
|
227
|
+
| `julialang upgrade [SPEC]` | install the target (latest by default), make it the default, delete the old default |
|
|
228
|
+
| `julialang downgrade SPEC` | the same towards an older version (spec required) |
|
|
229
|
+
| `julialang which` | path of the executable `julia` runs |
|
|
230
|
+
| `julialang --version` | version of this package |
|
|
231
|
+
|
|
232
|
+
The cached version list is trusted for one hour by `status`, `list` and `latest`; `install`, `upgrade`, `downgrade`
|
|
233
|
+
and `fetch` always try the network first and fall back to the cache when offline.
|
|
234
|
+
|
|
235
|
+
## Environment variables
|
|
236
|
+
|
|
237
|
+
| Variable | Effect |
|
|
238
|
+
| --- | --- |
|
|
239
|
+
| `JULIALANG_HOME` | where versions, `config.json` and the cache live (default `~/.julialang`) |
|
|
240
|
+
| `JULIALANG_VERSION` | spec of an installed version that `julia` should run, instead of the default |
|
|
241
|
+
| `JULIALANG_AUTO_INSTALL=1` | let `julia` install the latest version on first use |
|
|
242
|
+
| `JULIALANG_REPO` | GitHub repository to read the mirror from (default `MohammadRaziei/julialang`) |
|
|
243
|
+
| `JULIALANG_LIST_URL` | full URL of `julia-list.json`, overriding `JULIALANG_REPO` (useful for your own mirror or tests) |
|
|
244
|
+
|
|
245
|
+
State layout: `~/.julialang/versions/<version>/` holds each Julia, and `~/.julialang/config.json` records the default
|
|
246
|
+
and the installed versions; it is re-synchronised with the folders on every command, so you can also delete a folder by hand.
|
|
247
|
+
|
|
248
|
+
## How it works
|
|
249
|
+
|
|
250
|
+
Three kinds of GitHub releases act as storage, and two independent workflows do the work:
|
|
251
|
+
|
|
252
|
+
| Tag | Kind | Holds |
|
|
253
|
+
| --- | --- | --- |
|
|
254
|
+
| `__download__`, `__download_2__`, ... | prerelease | mirrored Julia archives (`julia-<version>-<os>-<arch>.<ext>`); `__download__` also holds **`julia-list.json`** (md5 + sha256 and URL of every file). GitHub allows ~1000 assets per release, so the mirror starts the next one after 900 files |
|
|
255
|
+
| `__beta__` | prerelease | the latest tested build of master (wheel + sdist) |
|
|
256
|
+
| `vX.Y.Z` | release | the published wheel and sdist |
|
|
257
|
+
|
|
258
|
+
`julia-list.json` is **not** in git: it is only a release asset, uploaded again after every mirrored version.
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
julialang.org (crawled) ──▶ sync.yml ──▶ __download__ (+ __download_N__) + julia-list.json asset
|
|
262
|
+
|
|
263
|
+
push to master ──▶ wheel.yml: ruff + pytest → build wheel/sdist → smoke test on 5 OSes
|
|
264
|
+
(pip install, `julialang install` FROM THE MIRROR, run julia) → __beta__
|
|
265
|
+
push of tag vX.Y.Z ──▶ the same, then → GitHub release vX.Y.Z + PyPI
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
`sync.yml` and `wheel.yml` never call each other.
|
|
269
|
+
|
|
270
|
+
- **`tools/sync.py`** crawls <https://julialang.org/downloads/manual-downloads/> and
|
|
271
|
+
<https://julialang.org/downloads/oldreleases/>, downloads every stable Julia that is not mirrored yet
|
|
272
|
+
(`--min-version` defaults to 0.0.0 = all), verifies it against the upstream sha256 (and md5 when published), uploads
|
|
273
|
+
it and records md5 + sha256 in `julia-list.json`. Only archives with a verifiable hash and a known name are mirrored:
|
|
274
|
+
`.tar.gz` for Linux/macOS, `.zip` for Windows (no installers, musl, FreeBSD, pre-releases). `--source json` uses the
|
|
275
|
+
official `versions.json` feed instead. A full backfill takes many hours, so the job stops itself before the 6 h limit
|
|
276
|
+
(`--max-minutes`); the next run (weekly cron, a push, or a manual start) continues where it stopped.
|
|
277
|
+
- **`sync.yml`** ("Sync Julia binaries") runs on every push, weekly and by hand. It only updates the mirror: it makes no
|
|
278
|
+
package release and starts no other workflow.
|
|
279
|
+
- **`wheel.yml`** ("Wheel") runs on every push to master and on tags `v*`. Its smoke test installs Julia from the mirror
|
|
280
|
+
on every OS, so it doubles as a health check of the mirror.
|
|
281
|
+
|
|
282
|
+
**Integrity.** `julialang install` checks the sha256 and md5 from `julia-list.json`. Both come from the same GitHub
|
|
283
|
+
repository as the archives, so this protects against corrupted downloads, not against someone taking over the repository.
|
|
284
|
+
|
|
285
|
+
## For maintainers
|
|
286
|
+
|
|
287
|
+
### Set up your own copy
|
|
288
|
+
|
|
289
|
+
1. Create the repo (default branch `master`; for a name other than `mohammadraziei/julialang` change `DEFAULT_REPO` in
|
|
290
|
+
`julialang/_state.py` and `tools/sync.py`, and the URLs in `pyproject.toml`) and push.
|
|
291
|
+
2. Settings → Actions → General → Workflow permissions: **Read and write**.
|
|
292
|
+
3. Every push starts **Sync Julia binaries**; run it by hand from the Actions tab for a `dry_run` (shows the plan) or to
|
|
293
|
+
mirror in slices with `limit`. The first full backfill needs several runs: start it again by hand (or wait for the
|
|
294
|
+
weekly cron) until nothing is left.
|
|
295
|
+
4. PyPI (optional): add the repository secret `PYPI_PASS` (an API token; for a new project use a token with the scope of
|
|
296
|
+
the whole account) under Settings → Secrets and variables → Actions. Secrets are per repository, so those of another
|
|
297
|
+
repo do not apply. `PYPI_USER` is only needed for a non-token login and must be `__token__` when `PYPI_PASS` is a
|
|
298
|
+
token.
|
|
299
|
+
|
|
300
|
+
### Release a new version
|
|
301
|
+
|
|
302
|
+
Versions are bumped by hand: edit `version` in `pyproject.toml`, commit, then tag and push.
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
git tag v0.1.0
|
|
306
|
+
git push origin master v0.1.0
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
The tag must equal the version in `pyproject.toml` (otherwise `wheel.yml` fails immediately). The tag run publishes the
|
|
310
|
+
GitHub release (with generated notes) and uploads to PyPI. Without the `PYPI_PASS` secret it prints a warning and skips
|
|
311
|
+
the upload: add the secret and re-run the tag's run (`twine upload --skip-existing` and the release step are safe to
|
|
312
|
+
repeat).
|
|
313
|
+
|
|
314
|
+
Without PyPI, the wheel installs straight from a GitHub release:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
pip install julialang --find-links https://github.com/MohammadRaziei/julialang/releases/expanded_assets/v0.1.0
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Develop
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
pip install -e . pytest setuptools wheel ruff
|
|
324
|
+
python -m ruff check .
|
|
325
|
+
python -m pytest -q tests # about a minute; the end-to-end test builds a wheel
|
|
326
|
+
python tools/sync.py --dry-run # show what would be mirrored
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Notes
|
|
330
|
+
|
|
331
|
+
- Julia is MIT-licensed, but its binary distribution bundles third-party libraries under their own licenses (see
|
|
332
|
+
`share/julia/` and `share/licenses` inside each version). The code in this repository is MIT.
|
|
333
|
+
- The `julia` command of this package may shadow another `julia` (or `juliaup`) earlier on your `PATH`; `julialang which`
|
|
334
|
+
shows what is actually run.
|
|
335
|
+
- GitHub pauses scheduled workflows after 60 days without repository activity; if the weekly sync stops, re-enable it in
|
|
336
|
+
the Actions tab.
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# julialang
|
|
4
|
+
|
|
5
|
+
**Install, switch and remove [Julia](https://julialang.org) versions with `pip`: no installer, no `apt`, no `juliaup`.**
|
|
6
|
+
|
|
7
|
+
[](https://pypi.org/project/julialang/)
|
|
8
|
+
[](https://pypi.org/project/julialang/)
|
|
9
|
+
[](https://github.com/MohammadRaziei/julialang/actions/workflows/wheel.yml)
|
|
10
|
+
[](https://github.com/MohammadRaziei/julialang/actions/workflows/sync.yml)
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install julialang && julialang install && julia
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Contents
|
|
22
|
+
|
|
23
|
+
- [Why julialang?](#why-julialang)
|
|
24
|
+
- [Quick start](#quick-start)
|
|
25
|
+
- [Everyday use](#everyday-use)
|
|
26
|
+
- [Install Julia](#install-julia) · [Run Julia](#run-julia) · [See what you have](#see-what-you-have) · [Switch version](#switch-version) · [Upgrade and downgrade](#upgrade-and-downgrade) · [Remove versions](#remove-versions) · [Remove everything](#remove-everything)
|
|
27
|
+
- [Version specs](#version-specs)
|
|
28
|
+
- [Command reference](#command-reference)
|
|
29
|
+
- [Environment variables](#environment-variables)
|
|
30
|
+
- [How it works](#how-it-works)
|
|
31
|
+
- [For maintainers](#for-maintainers)
|
|
32
|
+
- [Notes](#notes)
|
|
33
|
+
|
|
34
|
+
## Why julialang?
|
|
35
|
+
|
|
36
|
+
Like [`ziglang`](https://pypi.org/project/ziglang/) does for Zig, `julialang` makes Julia installable from Python
|
|
37
|
+
tooling.
|
|
38
|
+
|
|
39
|
+
- **Tiny wheel.** `pip install julialang` downloads a pure-Python wheel of a few KB (`py3-none-any`), the same on every platform.
|
|
40
|
+
- **No julialang.org needed.** The official binaries are mirrored on GitHub by CI; `julialang` downloads from there and verifies sha256 and md5.
|
|
41
|
+
- **Many versions side by side.** Install as many as you like, switch with one command, upgrade, downgrade, remove.
|
|
42
|
+
- **pip-style version specs.** `julialang install ">=1.10,<1.13"`, `julialang install 1.12`, `julialang install ~=1.12.2`.
|
|
43
|
+
- **Every stable release.** The mirror covers all stable Julia versions, from 0.x to the latest.
|
|
44
|
+
- **Zero dependencies.** Standard library only. Works on Linux (x86_64, aarch64, i686), macOS (x86_64, arm64) and Windows (x86_64, i686) with Python 3.9+.
|
|
45
|
+
|
|
46
|
+
## Quick start
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install julialang
|
|
50
|
+
julialang install # latest Julia, becomes the default
|
|
51
|
+
julia -e 'println("hello from Julia ", VERSION)'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Julia itself is **not** inside the wheel: `julialang install` downloads it into `~/.julialang`.
|
|
55
|
+
|
|
56
|
+
## Everyday use
|
|
57
|
+
|
|
58
|
+
### Install Julia
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
julialang install # the latest version
|
|
62
|
+
julialang install 1.12 # the newest 1.12.x
|
|
63
|
+
julialang install 1.10.5 # exactly this version
|
|
64
|
+
julialang install ">=1.10,<1.13" # the newest version that satisfies the spec
|
|
65
|
+
julialang install 1.10 1.12 # several at once
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`install` only **adds** versions: nothing is removed, and the installed version becomes the default. Pass
|
|
69
|
+
`--no-default` to keep the current default:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
julialang install 1.10 --no-default
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Run Julia
|
|
76
|
+
|
|
77
|
+
`julia` is a thin shim that runs the selected Julia with your arguments:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
julia # REPL
|
|
81
|
+
julia script.jl # run a script
|
|
82
|
+
julia -e 'print(VERSION)'
|
|
83
|
+
JULIALANG_VERSION=1.10 julia -e 'print(VERSION)' # one command with another installed version
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Which Julia runs: `JULIALANG_VERSION` (a spec, must be installed), else the default chosen by `julialang`, else the
|
|
87
|
+
newest installed one. The shim never touches the network. If nothing is installed yet it tells you to run
|
|
88
|
+
`julialang install` (set `JULIALANG_AUTO_INSTALL=1` to let `julia` install the latest on first use instead).
|
|
89
|
+
|
|
90
|
+
### See what you have
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
julialang # status: installed versions, the default, the latest on the mirror
|
|
94
|
+
julialang list # installed versions and the ones the mirror offers
|
|
95
|
+
julialang list --installed # only what is on your machine
|
|
96
|
+
julialang list --all # everything the mirror has
|
|
97
|
+
julialang latest # newest version on the mirror
|
|
98
|
+
julialang which # path of the executable `julia` runs
|
|
99
|
+
julialang fetch # refresh the cached list of mirrored versions now
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Switch version
|
|
103
|
+
|
|
104
|
+
Switch the default among the versions you have installed:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
julialang use 1.10
|
|
108
|
+
julia -e 'print(VERSION)' # 1.10.x
|
|
109
|
+
julialang use latest
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`use` never downloads: if the version is not installed it tells you to `julialang install` it first.
|
|
113
|
+
|
|
114
|
+
### Upgrade and downgrade
|
|
115
|
+
|
|
116
|
+
`install` keeps old versions; `upgrade` and `downgrade` **replace** the current default:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
julialang upgrade # install the latest, make it the default, remove the previous default
|
|
120
|
+
julialang upgrade 1.12 # ... or the newest 1.12.x
|
|
121
|
+
julialang downgrade 1.10 # same, towards an older version (the spec is required)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
The previous default is deleted only after the new version is downloaded and its checksum verified. Asking `upgrade`
|
|
125
|
+
for an older version (or `downgrade` for a newer one) is an error that points you to the other command; asking for
|
|
126
|
+
the version you already use just says so.
|
|
127
|
+
|
|
128
|
+
### Remove versions
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
julialang uninstall 1.12.2 # one version
|
|
132
|
+
julialang uninstall 1.10 1.11 # several
|
|
133
|
+
julialang uninstall "<1.12" --yes # a spec that matches many versions asks first; --yes skips the question
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Uninstalling the default promotes the newest remaining version to be the new default.
|
|
137
|
+
|
|
138
|
+
### Remove everything
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
rm -rf ~/.julialang # all installed Julia versions and the cache (Windows: delete %USERPROFILE%\.julialang)
|
|
142
|
+
pip uninstall julialang
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Installed versions live outside your virtual environment, so `pip uninstall julialang` alone leaves them in place.
|
|
146
|
+
|
|
147
|
+
## Version specs
|
|
148
|
+
|
|
149
|
+
Specs work like pip's, with npm-style handling of partial versions:
|
|
150
|
+
|
|
151
|
+
| Spec | Meaning |
|
|
152
|
+
| --- | --- |
|
|
153
|
+
| `latest` (or nothing) | the newest version |
|
|
154
|
+
| `1`, `1.12`, `1.12.*`, `==1.12` | the newest 1.x / 1.12.x |
|
|
155
|
+
| `1.12.4`, `==1.12.4` | exactly that version |
|
|
156
|
+
| `!=1.12.4` | anything but 1.12.4 |
|
|
157
|
+
| `>=1.10`, `<1.13` | comparison, as in pip |
|
|
158
|
+
| `<=1.12`, `>1.12` | with a partial version the whole line counts: `<=1.12` includes 1.12.x, `>1.12` starts at 1.13 |
|
|
159
|
+
| `~=1.12.2` | compatible release: `>=1.12.2,<1.13` (and `~=1.12` is `>=1.12,<2`) |
|
|
160
|
+
| `>=1.10,<1.13` | comma-joined: every part must hold, the newest match wins |
|
|
161
|
+
|
|
162
|
+
Quote specs that contain `<` or `>` so the shell does not treat them as redirection. Several words are joined with
|
|
163
|
+
commas, so `julialang install ">=1.10" "<1.13"` means the same as `julialang install ">=1.10,<1.13"`.
|
|
164
|
+
|
|
165
|
+
## Command reference
|
|
166
|
+
|
|
167
|
+
| Command | What it does |
|
|
168
|
+
| --- | --- |
|
|
169
|
+
| `julialang` / `julialang status` | installed versions, the default and the latest available |
|
|
170
|
+
| `julialang list [--all\|--installed]` | installed and mirrored versions |
|
|
171
|
+
| `julialang fetch` | refresh the cached list of mirrored versions |
|
|
172
|
+
| `julialang latest` | print the newest mirrored version |
|
|
173
|
+
| `julialang install [SPEC...] [--no-default]` | download and install (latest if no spec); makes it the default |
|
|
174
|
+
| `julialang uninstall SPEC... [--yes]` | delete versions (aliases: `remove`, `rm`) |
|
|
175
|
+
| `julialang use SPEC` | set the default among installed versions |
|
|
176
|
+
| `julialang upgrade [SPEC]` | install the target (latest by default), make it the default, delete the old default |
|
|
177
|
+
| `julialang downgrade SPEC` | the same towards an older version (spec required) |
|
|
178
|
+
| `julialang which` | path of the executable `julia` runs |
|
|
179
|
+
| `julialang --version` | version of this package |
|
|
180
|
+
|
|
181
|
+
The cached version list is trusted for one hour by `status`, `list` and `latest`; `install`, `upgrade`, `downgrade`
|
|
182
|
+
and `fetch` always try the network first and fall back to the cache when offline.
|
|
183
|
+
|
|
184
|
+
## Environment variables
|
|
185
|
+
|
|
186
|
+
| Variable | Effect |
|
|
187
|
+
| --- | --- |
|
|
188
|
+
| `JULIALANG_HOME` | where versions, `config.json` and the cache live (default `~/.julialang`) |
|
|
189
|
+
| `JULIALANG_VERSION` | spec of an installed version that `julia` should run, instead of the default |
|
|
190
|
+
| `JULIALANG_AUTO_INSTALL=1` | let `julia` install the latest version on first use |
|
|
191
|
+
| `JULIALANG_REPO` | GitHub repository to read the mirror from (default `MohammadRaziei/julialang`) |
|
|
192
|
+
| `JULIALANG_LIST_URL` | full URL of `julia-list.json`, overriding `JULIALANG_REPO` (useful for your own mirror or tests) |
|
|
193
|
+
|
|
194
|
+
State layout: `~/.julialang/versions/<version>/` holds each Julia, and `~/.julialang/config.json` records the default
|
|
195
|
+
and the installed versions; it is re-synchronised with the folders on every command, so you can also delete a folder by hand.
|
|
196
|
+
|
|
197
|
+
## How it works
|
|
198
|
+
|
|
199
|
+
Three kinds of GitHub releases act as storage, and two independent workflows do the work:
|
|
200
|
+
|
|
201
|
+
| Tag | Kind | Holds |
|
|
202
|
+
| --- | --- | --- |
|
|
203
|
+
| `__download__`, `__download_2__`, ... | prerelease | mirrored Julia archives (`julia-<version>-<os>-<arch>.<ext>`); `__download__` also holds **`julia-list.json`** (md5 + sha256 and URL of every file). GitHub allows ~1000 assets per release, so the mirror starts the next one after 900 files |
|
|
204
|
+
| `__beta__` | prerelease | the latest tested build of master (wheel + sdist) |
|
|
205
|
+
| `vX.Y.Z` | release | the published wheel and sdist |
|
|
206
|
+
|
|
207
|
+
`julia-list.json` is **not** in git: it is only a release asset, uploaded again after every mirrored version.
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
julialang.org (crawled) ──▶ sync.yml ──▶ __download__ (+ __download_N__) + julia-list.json asset
|
|
211
|
+
|
|
212
|
+
push to master ──▶ wheel.yml: ruff + pytest → build wheel/sdist → smoke test on 5 OSes
|
|
213
|
+
(pip install, `julialang install` FROM THE MIRROR, run julia) → __beta__
|
|
214
|
+
push of tag vX.Y.Z ──▶ the same, then → GitHub release vX.Y.Z + PyPI
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
`sync.yml` and `wheel.yml` never call each other.
|
|
218
|
+
|
|
219
|
+
- **`tools/sync.py`** crawls <https://julialang.org/downloads/manual-downloads/> and
|
|
220
|
+
<https://julialang.org/downloads/oldreleases/>, downloads every stable Julia that is not mirrored yet
|
|
221
|
+
(`--min-version` defaults to 0.0.0 = all), verifies it against the upstream sha256 (and md5 when published), uploads
|
|
222
|
+
it and records md5 + sha256 in `julia-list.json`. Only archives with a verifiable hash and a known name are mirrored:
|
|
223
|
+
`.tar.gz` for Linux/macOS, `.zip` for Windows (no installers, musl, FreeBSD, pre-releases). `--source json` uses the
|
|
224
|
+
official `versions.json` feed instead. A full backfill takes many hours, so the job stops itself before the 6 h limit
|
|
225
|
+
(`--max-minutes`); the next run (weekly cron, a push, or a manual start) continues where it stopped.
|
|
226
|
+
- **`sync.yml`** ("Sync Julia binaries") runs on every push, weekly and by hand. It only updates the mirror: it makes no
|
|
227
|
+
package release and starts no other workflow.
|
|
228
|
+
- **`wheel.yml`** ("Wheel") runs on every push to master and on tags `v*`. Its smoke test installs Julia from the mirror
|
|
229
|
+
on every OS, so it doubles as a health check of the mirror.
|
|
230
|
+
|
|
231
|
+
**Integrity.** `julialang install` checks the sha256 and md5 from `julia-list.json`. Both come from the same GitHub
|
|
232
|
+
repository as the archives, so this protects against corrupted downloads, not against someone taking over the repository.
|
|
233
|
+
|
|
234
|
+
## For maintainers
|
|
235
|
+
|
|
236
|
+
### Set up your own copy
|
|
237
|
+
|
|
238
|
+
1. Create the repo (default branch `master`; for a name other than `mohammadraziei/julialang` change `DEFAULT_REPO` in
|
|
239
|
+
`julialang/_state.py` and `tools/sync.py`, and the URLs in `pyproject.toml`) and push.
|
|
240
|
+
2. Settings → Actions → General → Workflow permissions: **Read and write**.
|
|
241
|
+
3. Every push starts **Sync Julia binaries**; run it by hand from the Actions tab for a `dry_run` (shows the plan) or to
|
|
242
|
+
mirror in slices with `limit`. The first full backfill needs several runs: start it again by hand (or wait for the
|
|
243
|
+
weekly cron) until nothing is left.
|
|
244
|
+
4. PyPI (optional): add the repository secret `PYPI_PASS` (an API token; for a new project use a token with the scope of
|
|
245
|
+
the whole account) under Settings → Secrets and variables → Actions. Secrets are per repository, so those of another
|
|
246
|
+
repo do not apply. `PYPI_USER` is only needed for a non-token login and must be `__token__` when `PYPI_PASS` is a
|
|
247
|
+
token.
|
|
248
|
+
|
|
249
|
+
### Release a new version
|
|
250
|
+
|
|
251
|
+
Versions are bumped by hand: edit `version` in `pyproject.toml`, commit, then tag and push.
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
git tag v0.1.0
|
|
255
|
+
git push origin master v0.1.0
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The tag must equal the version in `pyproject.toml` (otherwise `wheel.yml` fails immediately). The tag run publishes the
|
|
259
|
+
GitHub release (with generated notes) and uploads to PyPI. Without the `PYPI_PASS` secret it prints a warning and skips
|
|
260
|
+
the upload: add the secret and re-run the tag's run (`twine upload --skip-existing` and the release step are safe to
|
|
261
|
+
repeat).
|
|
262
|
+
|
|
263
|
+
Without PyPI, the wheel installs straight from a GitHub release:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
pip install julialang --find-links https://github.com/MohammadRaziei/julialang/releases/expanded_assets/v0.1.0
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Develop
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
pip install -e . pytest setuptools wheel ruff
|
|
273
|
+
python -m ruff check .
|
|
274
|
+
python -m pytest -q tests # about a minute; the end-to-end test builds a wheel
|
|
275
|
+
python tools/sync.py --dry-run # show what would be mirrored
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Notes
|
|
279
|
+
|
|
280
|
+
- Julia is MIT-licensed, but its binary distribution bundles third-party libraries under their own licenses (see
|
|
281
|
+
`share/julia/` and `share/licenses` inside each version). The code in this repository is MIT.
|
|
282
|
+
- The `julia` command of this package may shadow another `julia` (or `juliaup`) earlier on your `PATH`; `julialang which`
|
|
283
|
+
shows what is actually run.
|
|
284
|
+
- GitHub pauses scheduled workflows after 60 days without repository activity; if the weekly sync stops, re-enable it in
|
|
285
|
+
the Actions tab.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""julialang: `pip install julialang`, then `julialang install` and run `julia`."""
|
|
2
|
+
from importlib.metadata import PackageNotFoundError
|
|
3
|
+
from importlib.metadata import version as _version
|
|
4
|
+
|
|
5
|
+
try:
|
|
6
|
+
__version__ = _version("julialang")
|
|
7
|
+
except PackageNotFoundError: # running from a source checkout
|
|
8
|
+
__version__ = "0.0.0.dev0"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def julia_executable() -> str:
|
|
12
|
+
"""Path of the Julia executable that the `julia` command would run."""
|
|
13
|
+
from . import _state
|
|
14
|
+
|
|
15
|
+
return str(_state.exe_in(_state.resolve_root()))
|