Skip to content
Draft

v1 #673

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
743063c
add a section for v1 release notes
Nov 6, 2023
1df6867
Merge branch 'main' into v1
Nov 6, 2023
3ac5f4d
Merge branch 'main' into v1
Nov 7, 2023
63a3e60
make PythonCall.GC more like Base.GC (#413)
cjdoris Nov 7, 2023
d0cc3f7
Merge branch 'main' into v1
Nov 7, 2023
bd7e81a
Merge branch 'main' into v1
Mar 3, 2024
19119b6
Merge branch 'main' into v1
Mar 17, 2024
b81c389
Merge remote-tracking branch 'origin/main' into v1
May 4, 2024
63b2801
Merge remote-tracking branch 'origin/main' into v1
May 18, 2024
e8ebcea
Merge remote-tracking branch 'origin/main' into v1
Jun 10, 2024
eda0cc6
pyjl (#512)
cjdoris Jun 21, 2024
37a3c85
Merge remote-tracking branch 'origin/main' into v1
Aug 15, 2024
02bed07
fix tests for v1
Aug 15, 2024
2b0d376
Merge remote-tracking branch 'origin/main' into v1
Aug 15, 2024
4fa1228
test the gc queue is not empty while gil still unlocked
Aug 15, 2024
c995cb5
Merge remote-tracking branch 'origin/main' into v1
Sep 18, 2025
a996b4f
fixes after merge
Sep 18, 2025
090f05e
declare missing functions
Sep 18, 2025
06d3ad9
fix test for v1
Sep 18, 2025
f809e23
fix jl_help()
Sep 18, 2025
a29e591
not needed since v1 is a PR into main now
Sep 18, 2025
333b9a3
more REPL hacks
Sep 18, 2025
ae5a02d
Merge remote-tracking branch 'origin/main' into v1
Oct 23, 2025
59a16f9
comparisons return Bool not Py (#702)
cjdoris Oct 23, 2025
4b812e3
Delete unnecessary comparison operators (#698)
JamesWrigley Oct 23, 2025
d5f8ace
reparameterise PyArray (#674)
cjdoris Oct 23, 2025
03e2ed4
v1 migration guide (#704)
cjdoris Oct 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
- v1
tags:
- '*'
pull_request:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
pull_request:
branches:
- main
- v1
push:
branches:
- main
Expand Down
20 changes: 11 additions & 9 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
UnsafePointers = "e17b2a0c-0bdf-430a-bd0c-3a23cae4ff39"

[weakdeps]
CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597"
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"

[extensions]
CategoricalArraysExt = "CategoricalArrays"
PyCallExt = "PyCall"

[compat]
Aqua = "0 - 999"
CategoricalArrays = "0.10, 1"
Expand All @@ -24,27 +32,21 @@ MacroTools = "0.5"
Markdown = "1"
Pkg = "1"
PyCall = "1"
REPL = "1"
Serialization = "1"
Tables = "1"
Test = "1"
TestItemRunner = "0 - 999"
UnsafePointers = "1"
julia = "1.10"

[extensions]
PyCallExt = "PyCall"
CategoricalArraysExt = "CategoricalArrays"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597"
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a"

[targets]
test = ["Aqua", "PyCall", "Test", "TestItemRunner"]

[weakdeps]
CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597"
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
test = ["Aqua", "PyCall", "Test", "TestItemRunner", "REPL"]
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ makedocs(
"compat.md",
"faq.md",
"releasenotes.md",
"v1-migration-guide.md",
],
)

Expand Down
2 changes: 1 addition & 1 deletion docs/src/conversion-to-julia.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ From Python, the arguments to a Julia function will be converted according to th
| From | To |
| :----------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------- |
| **Top priority (wrapped values).** | |
| `juliacall.AnyValue` | `Any` |
| `juliacall.Jl` | `Any` |
| **Very high priority (arrays).** | |
| Objects satisfying the buffer or array interface (inc. `bytes`, `bytearray`, `array.array`, `numpy.ndarray`) | `PyArray` |
| **High priority (canonical conversions).** | |
Expand Down
23 changes: 6 additions & 17 deletions docs/src/conversion-to-python.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,13 @@ From Python, this occurs when converting the return value of a Julia function.
| `Dates.Time` | `datetime.time` |
| `Dates.DateTime` | `datetime.datetime` |
| `Dates.Second`, `Dates.Millisecond`, `Dates.Microsecond`, `Dates.Nanosecond` | `datetime.timedelta` |
| `Number` | `juliacall.NumberValue`, `juliacall.ComplexValue`, etc. |
| `AbstractArray` | `juliacall.ArrayValue`, `juliacall.VectorValue` |
| `AbstractDict` | `juliacall.DictValue` |
| `AbstractSet` | `juliacall.SetValue` |
| `IO` | `juliacall.BufferedIOValue` |
| `Module` | `juliacall.ModuleValue` |
| `Type` | `juliacall.TypeValue` |
| Anything else | `juliacall.AnyValue` |
| `AbstractArray` | `juliacall.JlArray`, `juliacall.JlVector` |
| `AbstractDict` | `juliacall.JlDict` |
| `AbstractSet` | `juliacall.JlSet` |
| `IO` | `juliacall.JlBinaryIO` |
| Anything else | `juliacall.Jl` |

See [here](@ref julia-wrappers) for an explanation of the `juliacall.*Value` wrapper types.
See [here](@ref julia-wrappers) for an explanation of the `juliacall.Jl*` wrapper types.

## [Custom rules](@id jl2py-conversion-custom)

Expand All @@ -47,11 +44,3 @@ object, then also define `ispy(::T) = true`.
```@docs
PythonCall.ispy
```

Alternatively, if you define a wrapper type (a subtype of
[`juliacall.AnyValue`](#juliacall.AnyValue)) then you may instead define `pyjltype(::T)` to
be that type.

```@docs
PythonCall.pyjltype
```
2 changes: 1 addition & 1 deletion docs/src/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Related issues:

## Issues when Numpy arrays are expected

When a Julia array is passed to Python, it is wrapped as a [`ArrayValue`](#juliacall.ArrayValue).
When a Julia array is passed to Python, it is wrapped as a [`JlArray`](#juliacall.JlArray).
This type satisfies the Numpy array interface and the buffer protocol, so can be used in
most places where a numpy array is valid.

Expand Down
165 changes: 52 additions & 113 deletions docs/src/juliacall-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
`````@customdoc
juliacall.Main - Constant

The Julia `Main` module, as a [`ModuleValue`](#juliacall.ModuleValue).
The Julia `Main` module, as a [`Jl`](#juliacall.Jl).

In interactive scripts, you can use this as the main entry-point to JuliaCall:
```python
Expand All @@ -20,18 +20,6 @@ The modules `Base`, `Core` and `PythonCall` are also available.

## Utilities

`````@customdoc
juliacall.convert - Function

```python
convert(T, x)
```

Convert `x` to a Julia object of type `T`.

You can use this to pass an argument to a Julia function of a specific type.
`````

`````@customdoc
juliacall.newmodule - Function

Expand All @@ -45,75 +33,71 @@ A new module with the given name.
## [Wrapper types](@id julia-wrappers)

Apart from a few fundamental immutable types, all Julia values are by default converted into
Python to some [`AnyValue`](#juliacall.AnyValue) object, which wraps the original value, but
giving it a Pythonic interface.

Subclasses of [`AnyValue`](#juliacall.AnyValue) provide additional Python semantics. For
example a Julia vector is converted to a [`VectorValue`](#juliacall.VectorValue) which
satisfies the Python sequence interface and behaves very similar to a list.

There is also a [`RawValue`](#juliacall.RawValue) object, which gives a stricter
"Julia-only" interface, documented below. These types all inherit from `ValueBase`:

- `ValueBase`
- [`RawValue`](#juliacall.RawValue)
- [`AnyValue`](#juliacall.AnyValue)
- [`NumberValue`](#juliacall.NumberValue)
- `ComplexValue`
- `RealValue`
- `RationalValue`
- `IntegerValue`
- [`ArrayValue`](#juliacall.ArrayValue)
- [`VectorValue`](#juliacall.VectorValue)
- [`DictValue`](#juliacall.DictValue)
- [`SetValue`](#juliacall.SetValue)
- [`IOValue`](#juliacall.IOValue)
- `BinaryIOValue`
- `TextIOValue`
- [`ModuleValue`](#juliacall.ModuleValue)
- [`TypeValue`](#juliacall.TypeValue)
Python to a [`Jl`](#juliacall.Jl) object, which wraps the original value and gives it a
Pythonic interface.

Other wrapper classes provide more specific Python semantics. For example a Julia vector can
be converted to a [`JlVector`](#juliacall.JlVector) which satisfies the Python sequence
interface and behaves very similar to a list.

- `JlBase`
- [`Jl`](#juliacall.Jl)
- [`JlCollection`](#juliacall.JlCollection)
- [`JlArray`](#juliacall.JlArray)
- [`JlVector`](#juliacall.JlVector)
- [`JlDict`](#juliacall.JlDict)
- [`JlSet`](#juliacall.JlSet)
- [`JlIOBase`](#juliacall.JlIOBase)
- `JlBinaryIO`
- `JlTextIO`

`````@customdoc
juliacall.AnyValue - Class
juliacall.Jl - Class

Wraps any Julia object, giving it some basic Python semantics. Subtypes provide extra
semantics.
Wraps any Julia object, giving it some basic Python semantics.

Supports `repr(x)`, `str(x)`, attributes (`x.attr`), calling (`x(a,b)`), iteration,
comparisons, `len(x)`, `a in x`, `dir(x)`.

Calling, indexing, attribute access, etc. will convert the result to a Python object
according to [this table](@ref jl2py). This is typically a builtin Python type (for
immutables) or a subtype of `AnyValue`.
Calling, indexing, attribute access, etc. will always return a `Jl`. To get the result
as an ordinary Python object, you can use the `.jl_to_py()` method.

Attribute access can be used to access Julia properties as well as normal class members. In
the case of a name clash, the class member will take precedence. For convenience with Julia
naming conventions, `_b` at the end of an attribute is replaced with `!` and `_bb` is
replaced with `!!`.
Attribute access (`x.attr`) can be used to access Julia properties except those starting
and ending with `__` (since these are Python special methods) or starting with `jl_` or
`_jl_` (which are reserved by `juliacall` for Julia-specific methods).

###### Members
- `_jl_raw()`: Convert to a [`RawValue`](#juliacall.RawValue). (See also [`pyjlraw`](@ref).)
- `_jl_display(mime=None)`: Display the object using Julia's display mechanism.
- `_jl_help(mime=None)`: Display help for the object.
- `_jl_call_nogil(*args, **kwargs)`: Call this with the GIL disabled.
- `jl_callback(*args, **kwargs)`: Calls the Julia object with the given arguments.
Unlike ordinary calling syntax, the arguments are passed as `Py` objects instead of
being converted.
- `jl_call_nogil(*args, **kwargs)`: Call this with the GIL disabled.
- `jl_display()`: Display the object using Julia's display mechanism.
- `jl_eval(expr)`: If the object is a Julia `Module`, evaluates the given expression.
- `jl_help()`: Display help for the object.
- `jl_to_py()`: Convert to a Python object using the [usual conversion rules](@ref jl2py).
`````

`````@customdoc
juliacall.NumberValue - Class
juliacall.JlCollection - Class

Wraps any Julia collection. It is a subclass of `collections.abc.Collection`.

This wraps any Julia `Number` value. It is a subclass of `numbers.Number` and behaves
similar to other Python numbers.
Julia collections are arrays, sets, dicts, tuples, named tuples, refs, and in general
anything which is a collection of values in the sense that it supports functions like
`iterate`, `in`, `length`, `hash`, `==`, `isempty`, `copy`, `empty!`.

There are also subtypes `ComplexValue`, `RealValue`, `RationalValue`, `IntegerValue` which
wrap values of the corresponding Julia types, and are subclasses of the corresponding
`numbers` ABC.
It supports `in`, `iter`, `len`, `hash`, `bool`, `==`.

###### Members
- `clear()`: Empty the collection in-place.
- `copy()`: A copy of the collection.
`````

`````@customdoc
juliacall.ArrayValue - Class
juliacall.JlArray - Class

This wraps any Julia `AbstractArray` value. It is a subclass of
`collections.abc.Collection`.
`juliacall.JlCollection`.

It supports zero-up indexing, and can be indexed with integers or slices. Slicing returns a
view of the original array.
Expand All @@ -131,22 +115,20 @@ copy of the original array.
###### Members
- `ndim`: The number of dimensions.
- `shape`: Tuple of lengths in each dimension.
- `copy()`: A copy of the array.
- `reshape(shape)`: A reshaped view of the array.
- `to_numpy(dtype=None, copy=True, order="K")`: Convert to a numpy array.
`````

`````@customdoc
juliacall.VectorValue - Class
juliacall.JlVector - Class

This wraps any Julia `AbstractVector` value. It is a subclass of `juliacall.ArrayValue` and
This wraps any Julia `AbstractVector` value. It is a subclass of `juliacall.JlArray` and
`collections.abc.MutableSequence` and behaves similar to a Python `list`.

###### Members
- `resize(size)`: Change the length of the vector.
- `sort(reverse=False, key=None)`: Sort the vector in-place.
- `reverse()`: Reverse the vector.
- `clear()`: Empty the vector.
- `insert(index, value)`: Insert the value at the given index.
- `append(value)`: Append the value to the end of the vector.
- `extend(values)`: Append the values to the end of the vector.
Expand All @@ -157,66 +139,23 @@ This wraps any Julia `AbstractVector` value. It is a subclass of `juliacall.Arra
`````

`````@customdoc
juliacall.DictValue - Class
juliacall.JlDict - Class
This wraps any Julia `AbstractDict` value. It is a subclass of `collections.abc.MutableMapping` and
behaves similar to a Python `dict`.
`````

`````@customdoc
juliacall.SetValue - Class
juliacall.JlSet - Class
This wraps any Julia `AbstractSet` value. It is a subclass of `collections.abc.MutableSet` and
behaves similar to a Python `set`.
`````

`````@customdoc
juliacall.IOValue - Class
juliacall.JlIOBase - Class

This wraps any Julia `IO` value. It is a subclass of `io.IOBase` and behaves like Python
files.

There are also subtypes `BinaryIOValue` and `TextIOValue`, which are subclasses of
There are also subtypes `JlBinaryIO` and `JlTextIO`, which are subclasses of
`io.BufferedIOBase` (buffered bytes) and `io.TextIOBase` (text).
`````

`````@customdoc
juliacall.ModuleValue - Class
This wraps any Julia `Module` value.

It is the same as [`AnyValue`](#juliacall.AnyValue) except for one additional convenience
method:
- `seval([module=self], code)`: Evaluates the given code (a string) in the given module.
`````

`````@customdoc
juliacall.TypeValue - Class

This wraps any Julia `Type` value.

It is the same as [`AnyValue`](#juliacall.AnyValue) except that indexing is used to access
Julia's "curly" syntax for specifying parametric types:

```python
from juliacall import Main as jl
# equivalent to Vector{Int}() in Julia
jl.Vector[jl.Int]()
```
`````

`````@customdoc
juliacall.RawValue - Class

Wraps any Julia value with a rigid interface suitable for generic programming.

Supports `repr(x)`, `str(x)`, attributes (`x.attr`), calling (`x(a,b)`), `len(x)`, `dir(x)`.

This is very similar to [`AnyValue`](#juliacall.AnyValue) except that indexing, calling,
etc. will always return a `RawValue`.

Indexing with a tuple corresponds to indexing in Julia with multiple values. To index with a
single tuple, it will need to be wrapped in another tuple.

###### Members
- `_jl_any()`: Convert to a [`AnyValue`](#juliacall.AnyValue) (or subclass). (See also
[`pyjl`](@ref).)
- `_jl_call_nogil(*args, **kwargs)`: Call this with the GIL disabled.
`````
6 changes: 3 additions & 3 deletions docs/src/juliacall.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ import juliacall
jl = juliacall.newmodule("SomeName")
```

Julia modules have a special method `seval` which will evaluate a given piece of code given
Julia modules have a special method `jl_eval` which will evaluate a given piece of code given
as a string in the module. This is most frequently used to import modules:
```python
from array import array
jl.seval("using Statistics")
jl.jl_eval("using Statistics")
x = array('i', [1, 2, 3])
jl.mean(x)
# 2.0
Expand All @@ -69,7 +69,7 @@ jl.cor(x, y)
```

What to read next:
- The main functionality of this package is in `AnyValue` objects, which represent Julia
- The main functionality of this package is in `Jl` objects, which represent Julia
objects, [documented here](@ref julia-wrappers).
- If you need to install Julia packages, [read here](@ref julia-deps).
- When you call a Julia function, such as `jl.rand(...)` in the above example, its
Expand Down
Loading
Loading