Skip to content

Commit eda0cc6

Browse files
authored
pyjl (#512)
* change julia wrapper hierarchy * remove RawValue, TypeValue and ModuleValue from juliacall * add JlBase.__init__ and store nothing/true/false specially * more general numeric operators * rounding methods, jl_to_py, remove jlwrap numbers * update release notes * remove references to nonexistent types * add jl_callback * simpler iteration * simplify callbacks (pyfunc etc) * change to pyjl(x) * rename ArrayValue to JlArray etc * __init__ for all * mixins for consistency * improve pymacro docstring * jlwrap tests * remove old tests * pyjlset tests * pyjldict tests * pyjlio tests * pyjlarray tests * pyjlvector tests * pyjl hash, == and iter * add JlContainer * eliminate mixins and add contains and clear to JlCollection * fix keys, values, items * fix tests * tests for JlCollection * update jlwrap docs * Jl tests * JlDict tests * PySet tests * JlIO tests * more JlIO tests * rename julia wrapper classes * enable CI on v1 branch * Jl.jl_eval fix return type * adapt python tests to latest version * propagate seval -> jl_eval --------- Co-authored-by: Christopher Doris <github.com/cjdoris>
1 parent e8ebcea commit eda0cc6

36 files changed

+1630
-1177
lines changed

.github/workflows/docs.yml

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- main
7+
- v1
78
tags:
89
- '*'
910
pull_request:

.github/workflows/tests.yml

+2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ on:
44
pull_request:
55
branches:
66
- main
7+
- v1
78
push:
89
branches:
910
- main
11+
- v1
1012
tags:
1113
- '*'
1214

docs/src/conversion-to-julia.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ From Python, the arguments to a Julia function will be converted according to th
1111
| From | To |
1212
| :----------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------- |
1313
| **Top priority (wrapped values).** | |
14-
| `juliacall.AnyValue` | `Any` |
14+
| `juliacall.Jl` | `Any` |
1515
| **Very high priority (arrays).** | |
1616
| Objects satisfying the buffer or array interface (inc. `bytes`, `bytearray`, `array.array`, `numpy.ndarray`) | `PyArray` |
1717
| **High priority (canonical conversions).** | |

docs/src/conversion-to-python.md

+6-17
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,13 @@ From Python, this occurs when converting the return value of a Julia function.
2323
| Standard integer range (`AbstractRange{T}`, `T` a standard integer) | `range` |
2424
| `Date`, `Time`, `DateTime` (from `Dates`) | `date`, `time`, `datetime` (from `datetime`) |
2525
| `Second`, `Millisecond`, `Microsecond`, `Nanosecond` (from `Dates`) | `timedelta` (from `datetime`) |
26-
| `Number` | `juliacall.NumberValue`, `juliacall.ComplexValue`, etc. |
27-
| `AbstractArray` | `juliacall.ArrayValue`, `juliacall.VectorValue` |
28-
| `AbstractDict` | `juliacall.DictValue` |
29-
| `AbstractSet` | `juliacall.SetValue` |
30-
| `IO` | `juliacall.BufferedIOValue` |
31-
| `Module` | `juliacall.ModuleValue` |
32-
| `Type` | `juliacall.TypeValue` |
33-
| Anything else | `juliacall.AnyValue` |
26+
| `AbstractArray` | `juliacall.JlArray`, `juliacall.JlVector` |
27+
| `AbstractDict` | `juliacall.JlDict` |
28+
| `AbstractSet` | `juliacall.JlSet` |
29+
| `IO` | `juliacall.JlBinaryIO` |
30+
| Anything else | `juliacall.Jl` |
3431

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

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

@@ -44,11 +41,3 @@ object, then also define `ispy(::T) = true`.
4441
```@docs
4542
PythonCall.ispy
4643
```
47-
48-
Alternatively, if you define a wrapper type (a subtype of
49-
[`juliacall.AnyValue`](#juliacall.AnyValue)) then you may instead define `pyjltype(::T)` to
50-
be that type.
51-
52-
```@docs
53-
PythonCall.pyjltype
54-
```

docs/src/faq.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Related issues: [#201](https://github.com/JuliaPy/PythonCall.jl/issues/201), [#2
2020

2121
## Issues when Numpy arrays are expected
2222

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

docs/src/juliacall-reference.md

+51-111
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
`````@customdoc
66
juliacall.Main - Constant
77
8-
The Julia `Main` module, as a [`ModuleValue`](#juliacall.ModuleValue).
8+
The Julia `Main` module, as a [`Jl`](#juliacall.Jl).
99
1010
In interactive scripts, you can use this as the main entry-point to JuliaCall:
1111
```python
@@ -20,18 +20,6 @@ The modules `Base`, `Core` and `PythonCall` are also available.
2020

2121
## Utilities
2222

23-
`````@customdoc
24-
juliacall.convert - Function
25-
26-
```python
27-
convert(T, x)
28-
```
29-
30-
Convert `x` to a Julia object of type `T`.
31-
32-
You can use this to pass an argument to a Julia function of a specific type.
33-
`````
34-
3523
`````@customdoc
3624
juliacall.newmodule - Function
3725
@@ -45,74 +33,70 @@ A new module with the given name.
4533
## [Wrapper types](@id julia-wrappers)
4634

4735
Apart from a few fundamental immutable types, all Julia values are by default converted into
48-
Python to some [`AnyValue`](#juliacall.AnyValue) object, which wraps the original value, but
49-
giving it a Pythonic interface.
50-
51-
Subclasses of [`AnyValue`](#juliacall.AnyValue) provide additional Python semantics. For
52-
example a Julia vector is converted to a [`VectorValue`](#juliacall.VectorValue) which
53-
satisfies the Python sequence interface and behaves very similar to a list.
54-
55-
There is also a [`RawValue`](#juliacall.RawValue) object, which gives a stricter
56-
"Julia-only" interface, documented below. These types all inherit from `ValueBase`:
57-
58-
- `ValueBase`
59-
- [`RawValue`](#juliacall.RawValue)
60-
- [`AnyValue`](#juliacall.AnyValue)
61-
- [`NumberValue`](#juliacall.NumberValue)
62-
- `ComplexValue`
63-
- `RealValue`
64-
- `RationalValue`
65-
- `IntegerValue`
66-
- [`ArrayValue`](#juliacall.ArrayValue)
67-
- [`VectorValue`](#juliacall.VectorValue)
68-
- [`DictValue`](#juliacall.DictValue)
69-
- [`SetValue`](#juliacall.SetValue)
70-
- [`IOValue`](#juliacall.IOValue)
71-
- `BinaryIOValue`
72-
- `TextIOValue`
73-
- [`ModuleValue`](#juliacall.ModuleValue)
74-
- [`TypeValue`](#juliacall.TypeValue)
36+
Python to a [`Jl`](#juliacall.Jl) object, which wraps the original value and gives it a
37+
Pythonic interface.
38+
39+
Other wrapper classes provide more specific Python semantics. For example a Julia vector can
40+
be converted to a [`JlVector`](#juliacall.JlVector) which satisfies the Python sequence
41+
interface and behaves very similar to a list.
42+
43+
- `JlBase`
44+
- [`Jl`](#juliacall.Jl)
45+
- [`JlCollection`](#juliacall.JlCollection)
46+
- [`JlArray`](#juliacall.JlArray)
47+
- [`JlVector`](#juliacall.JlVector)
48+
- [`JlDict`](#juliacall.JlDict)
49+
- [`JlSet`](#juliacall.JlSet)
50+
- [`JlIOBase`](#juliacall.JlIOBase)
51+
- `JlBinaryIO`
52+
- `JlTextIO`
7553

7654
`````@customdoc
77-
juliacall.AnyValue - Class
55+
juliacall.Jl - Class
7856
79-
Wraps any Julia object, giving it some basic Python semantics. Subtypes provide extra
80-
semantics.
57+
Wraps any Julia object, giving it some basic Python semantics.
8158
8259
Supports `repr(x)`, `str(x)`, attributes (`x.attr`), calling (`x(a,b)`), iteration,
8360
comparisons, `len(x)`, `a in x`, `dir(x)`.
8461
85-
Calling, indexing, attribute access, etc. will convert the result to a Python object
86-
according to [this table](@ref jl2py). This is typically a builtin Python type (for
87-
immutables) or a subtype of `AnyValue`.
62+
Calling, indexing, attribute access, etc. will always return a `Jl`. To get the result
63+
as an ordinary Python object, you can use the `.jl_to_py()` method.
8864
89-
Attribute access can be used to access Julia properties as well as normal class members. In
90-
the case of a name clash, the class member will take precedence. For convenience with Julia
91-
naming conventions, `_b` at the end of an attribute is replaced with `!` and `_bb` is
92-
replaced with `!!`.
65+
Attribute access (`x.attr`) can be used to access Julia properties except those starting
66+
and ending with `__` (since these are Python special methods) or starting with `jl_` or
67+
`_jl_` (which are reserved by `juliacall` for Julia-specific methods).
9368
9469
###### Members
95-
- `_jl_raw()`: Convert to a [`RawValue`](#juliacall.RawValue). (See also [`pyjlraw`](@ref).)
96-
- `_jl_display()`: Display the object using Julia's display mechanism.
97-
- `_jl_help()`: Display help for the object.
70+
- `jl_callback(*args, **kwargs)`: Calls the Julia object with the given arguments.
71+
Unlike ordinary calling syntax, the arguments are passed as `Py` objects instead of
72+
being converted.
73+
- `jl_display()`: Display the object using Julia's display mechanism.
74+
- `jl_eval(expr)`: If the object is a Julia `Module`, evaluates the given expression.
75+
- `jl_help()`: Display help for the object.
76+
- `jl_to_py()`: Convert to a Python object using the [usual conversion rules](@ref jl2py).
9877
`````
9978

10079
`````@customdoc
101-
juliacall.NumberValue - Class
80+
juliacall.JlCollection - Class
81+
82+
Wraps any Julia collection. It is a subclass of `collections.abc.Collection`.
10283
103-
This wraps any Julia `Number` value. It is a subclass of `numbers.Number` and behaves
104-
similar to other Python numbers.
84+
Julia collections are arrays, sets, dicts, tuples, named tuples, refs, and in general
85+
anything which is a collection of values in the sense that it supports functions like
86+
`iterate`, `in`, `length`, `hash`, `==`, `isempty`, `copy`, `empty!`.
10587
106-
There are also subtypes `ComplexValue`, `RealValue`, `RationalValue`, `IntegerValue` which
107-
wrap values of the corresponding Julia types, and are subclasses of the corresponding
108-
`numbers` ABC.
88+
It supports `in`, `iter`, `len`, `hash`, `bool`, `==`.
89+
90+
###### Members
91+
- `clear()`: Empty the collection in-place.
92+
- `copy()`: A copy of the collection.
10993
`````
11094

11195
`````@customdoc
112-
juliacall.ArrayValue - Class
96+
juliacall.JlArray - Class
11397
11498
This wraps any Julia `AbstractArray` value. It is a subclass of
115-
`collections.abc.Collection`.
99+
`juliacall.JlCollection`.
116100
117101
It supports zero-up indexing, and can be indexed with integers or slices. Slicing returns a
118102
view of the original array.
@@ -130,22 +114,20 @@ copy of the original array.
130114
###### Members
131115
- `ndim`: The number of dimensions.
132116
- `shape`: Tuple of lengths in each dimension.
133-
- `copy()`: A copy of the array.
134117
- `reshape(shape)`: A reshaped view of the array.
135118
- `to_numpy(dtype=None, copy=True, order="K")`: Convert to a numpy array.
136119
`````
137120

138121
`````@customdoc
139-
juliacall.VectorValue - Class
122+
juliacall.JlVector - Class
140123
141-
This wraps any Julia `AbstractVector` value. It is a subclass of `juliacall.ArrayValue` and
124+
This wraps any Julia `AbstractVector` value. It is a subclass of `juliacall.JlArray` and
142125
`collections.abc.MutableSequence` and behaves similar to a Python `list`.
143126
144127
###### Members
145128
- `resize(size)`: Change the length of the vector.
146129
- `sort(reverse=False, key=None)`: Sort the vector in-place.
147130
- `reverse()`: Reverse the vector.
148-
- `clear()`: Empty the vector.
149131
- `insert(index, value)`: Insert the value at the given index.
150132
- `append(value)`: Append the value to the end of the vector.
151133
- `extend(values)`: Append the values to the end of the vector.
@@ -156,65 +138,23 @@ This wraps any Julia `AbstractVector` value. It is a subclass of `juliacall.Arra
156138
`````
157139

158140
`````@customdoc
159-
juliacall.DictValue - Class
141+
juliacall.JlDict - Class
160142
This wraps any Julia `AbstractDict` value. It is a subclass of `collections.abc.Mapping` and
161143
behaves similar to a Python `dict`.
162144
`````
163145

164146
`````@customdoc
165-
juliacall.SetValue - Class
147+
juliacall.JlSet - Class
166148
This wraps any Julia `AbstractSet` value. It is a subclass of `collections.abc.Set` and
167149
behaves similar to a Python `set`.
168150
`````
169151

170152
`````@customdoc
171-
juliacall.IOValue - Class
153+
juliacall.JlIOBase - Class
172154
173155
This wraps any Julia `IO` value. It is a subclass of `io.IOBase` and behaves like Python
174156
files.
175157
176-
There are also subtypes `BinaryIOValue` and `TextIOValue`, which are subclasses of
158+
There are also subtypes `JlBinaryIO` and `JlTextIO`, which are subclasses of
177159
`io.BufferedIOBase` (buffered bytes) and `io.TextIOBase` (text).
178160
`````
179-
180-
`````@customdoc
181-
juliacall.ModuleValue - Class
182-
This wraps any Julia `Module` value.
183-
184-
It is the same as [`AnyValue`](#juliacall.AnyValue) except for one additional convenience
185-
method:
186-
- `seval([module=self], code)`: Evaluates the given code (a string) in the given module.
187-
`````
188-
189-
`````@customdoc
190-
juliacall.TypeValue - Class
191-
192-
This wraps any Julia `Type` value.
193-
194-
It is the same as [`AnyValue`](#juliacall.AnyValue) except that indexing is used to access
195-
Julia's "curly" syntax for specifying parametric types:
196-
197-
```python
198-
from juliacall import Main as jl
199-
# equivalent to Vector{Int}() in Julia
200-
jl.Vector[jl.Int]()
201-
```
202-
`````
203-
204-
`````@customdoc
205-
juliacall.RawValue - Class
206-
207-
Wraps any Julia value with a rigid interface suitable for generic programming.
208-
209-
Supports `repr(x)`, `str(x)`, attributes (`x.attr`), calling (`x(a,b)`), `len(x)`, `dir(x)`.
210-
211-
This is very similar to [`AnyValue`](#juliacall.AnyValue) except that indexing, calling,
212-
etc. will always return a `RawValue`.
213-
214-
Indexing with a tuple corresponds to indexing in Julia with multiple values. To index with a
215-
single tuple, it will need to be wrapped in another tuple.
216-
217-
###### Members
218-
- `_jl_any()`: Convert to a [`AnyValue`](#juliacall.AnyValue) (or subclass). (See also
219-
[`pyjl`](@ref).)
220-
`````

docs/src/juliacall.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ import juliacall
5050
jl = juliacall.newmodule("SomeName")
5151
```
5252

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

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

docs/src/pycall.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Furthermore, `pyconvert` can be extended to support more types, whereas `convert
2626

2727
Both packages allow conversion of Julia values to Python: `PyObject(x)` in PyCall, `Py(x)` in PythonCall.
2828

29-
Whereas both packages convert numbers, booleans, tuples and strings to their Python counterparts, they differ in handling other types. For example PyCall converts `AbstractVector` to `list` whereas PythonCall converts `AbstractVector` to `juliacall.VectorValue` which is a sequence type directly wrapping the Julia value - this has the advantage that mutating the Python object also mutates the original Julia object.
29+
Whereas both packages convert numbers, booleans, tuples and strings to their Python counterparts, they differ in handling other types. For example PyCall converts `AbstractVector` to `list` whereas PythonCall converts `AbstractVector` to `juliacall.JlVector` which is a sequence type directly wrapping the Julia value - this has the advantage that mutating the Python object also mutates the original Julia object.
3030

3131
Hence with PyCall the following does not mutate the original array `x`:
3232
```julia

docs/src/pythoncall-reference.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,13 @@ conversion to Python, unless the value is immutable and has a corresponding Pyth
8888

8989
```@docs
9090
pyjl
91-
pyjlraw
9291
pyisjl
9392
pyjlvalue
9493
pybinaryio
9594
pytextio
95+
pyjlarray
96+
pyjlset
97+
pyjldict
9698
```
9799

98100
## Arithmetic

docs/src/releasenotes.md

+11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
## Unreleased (v1)
44
* `PythonCall.GC` is now more like `Base.GC`: `enable(true)` replaces `enable()`, `enable(false)` replaces `disable()`, and `gc()` is added.
5+
* Breaking changes to Julia wrapper types:
6+
* Classes renamed: `ValueBase` to `JlBase`, `AnyValue` to `Jl`, `ArrayValue` to `JlArray`, etc.
7+
* Classes removed: `RawValue`, `ModuleValue`, `TypeValue`, `NumberValue`, `ComplexValue`, `RealValue`, `RationalValue`, `IntegerValue`.
8+
* `Jl` now behaves similar to how `RawValue` behaved before. In particular, most methods on `Jl` now return a `Jl` instead of an arbitrary Python object.
9+
* `juliacall.Pkg` removed (you can import it yourself).
10+
* `juliacall.convert` removed (use `juliacall.Jl` instead).
11+
* Methods renamed: `_jl_display()` to `jl_display()`, `_jl_help()` to `jl_help()`, etc.
12+
* Methods removed: `_jl_raw()`.
13+
* `pyjl(x)` now always returns a `juliacall.Jl` (it used to select a wrapper type if possible).
14+
* `pyjltype(x)` removed.
15+
* New functions: `pyjlarray`, `pyjldict`, `pyjlset`.
516

617
## Unreleased
718
* `Serialization.serialize` can use `dill` instead of `pickle` by setting the env var `JULIA_PYTHONCALL_PICKLE=dill`.

examples/flux.ipynb

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@
3131
"metadata": {},
3232
"outputs": [],
3333
"source": [
34-
"jl.seval(\"using Flux\")\n",
34+
"jl.jl_eval(\"using Flux\")\n",
3535
"model = jl.Chain(\n",
3636
" jl.Dense(1, 10, jl.relu),\n",
3737
" jl.Dense(10, 10, jl.relu),\n",
3838
" jl.Dense(10, 10, jl.relu),\n",
3939
" jl.Dense(10, 1),\n",
4040
")\n",
41-
"loss = jl.seval(\"m -> (x, y) -> Flux.Losses.mse(m(x), y)\")(model)"
41+
"loss = jl.jl_eval(\"m -> (x, y) -> Flux.Losses.mse(m(x), y)\")(model)"
4242
]
4343
},
4444
{

0 commit comments

Comments
 (0)