Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pyjl #512

Merged
merged 40 commits into from
Jun 21, 2024
Merged

pyjl #512

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7e8d373
change julia wrapper hierarchy
May 4, 2024
a3a2a91
remove RawValue, TypeValue and ModuleValue from juliacall
May 4, 2024
35e2424
add JlBase.__init__ and store nothing/true/false specially
May 4, 2024
a3b7d5b
more general numeric operators
May 4, 2024
7691d99
rounding methods, jl_to_py, remove jlwrap numbers
May 4, 2024
6b6d3de
update release notes
May 4, 2024
bfc84e7
remove references to nonexistent types
May 4, 2024
6b47a0e
add jl_callback
May 4, 2024
f34317d
simpler iteration
May 4, 2024
a96820f
simplify callbacks (pyfunc etc)
May 5, 2024
4931e79
change to pyjl(x)
May 5, 2024
398c4ff
rename ArrayValue to JlArray etc
May 5, 2024
f82be56
__init__ for all
May 5, 2024
747a331
mixins for consistency
May 5, 2024
526f044
improve pymacro docstring
May 12, 2024
4603fdf
Merge branch 'v1' into pyjl2
Jun 10, 2024
49645fc
jlwrap tests
Jun 10, 2024
4f67c76
remove old tests
Jun 10, 2024
157d725
pyjlset tests
Jun 10, 2024
c271815
pyjldict tests
Jun 10, 2024
5e79f89
pyjlio tests
Jun 10, 2024
6b1cdd2
pyjlarray tests
Jun 10, 2024
1ff9df6
pyjlvector tests
Jun 10, 2024
034f487
pyjl hash, == and iter
Jun 11, 2024
201fbf7
add JlContainer
Jun 11, 2024
8391bd6
eliminate mixins and add contains and clear to JlCollection
Jun 11, 2024
192d7d5
fix keys, values, items
Jun 11, 2024
6ee17e1
fix tests
Jun 11, 2024
a76e3d1
tests for JlCollection
Jun 12, 2024
d998031
update jlwrap docs
Jun 12, 2024
128071a
Jl tests
Jun 19, 2024
93b6ea8
JlDict tests
Jun 19, 2024
09c760c
PySet tests
Jun 19, 2024
674ab2c
JlIO tests
Jun 19, 2024
77b5ee1
more JlIO tests
Jun 20, 2024
e88de1e
rename julia wrapper classes
Jun 20, 2024
4edab03
enable CI on v1 branch
Jun 20, 2024
8057efb
Jl.jl_eval fix return type
Jun 20, 2024
e5661bb
adapt python tests to latest version
Jun 20, 2024
dba0f37
propagate seval -> jl_eval
Jun 20, 2024
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
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ on:
pull_request:
branches:
- main
- v1
push:
branches:
- main
- v1
tags:
- '*'

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 @@ -23,16 +23,13 @@ From Python, this occurs when converting the return value of a Julia function.
| Standard integer range (`AbstractRange{T}`, `T` a standard integer) | `range` |
| `Date`, `Time`, `DateTime` (from `Dates`) | `date`, `time`, `datetime` (from `datetime`) |
| `Second`, `Millisecond`, `Microsecond`, `Nanosecond` (from `Dates`) | `timedelta` (from `datetime`) |
| `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 @@ -44,11 +41,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 @@ -20,7 +20,7 @@ Related issues: [#201](https://github.com/JuliaPy/PythonCall.jl/issues/201), [#2

## 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
162 changes: 51 additions & 111 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,74 +33,70 @@ 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()`: Display the object using Julia's display mechanism.
- `_jl_help()`: Display help for the object.
- `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_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 @@ -130,22 +114,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 @@ -156,65 +138,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.Mapping` 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.Set` 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).)
`````
6 changes: 3 additions & 3 deletions docs/src/juliacall.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,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 @@ -64,7 +64,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
2 changes: 1 addition & 1 deletion docs/src/pycall.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Furthermore, `pyconvert` can be extended to support more types, whereas `convert

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

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.
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.

Hence with PyCall the following does not mutate the original array `x`:
```julia
Expand Down
4 changes: 3 additions & 1 deletion docs/src/pythoncall-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,13 @@ conversion to Python, unless the value is immutable and has a corresponding Pyth

```@docs
pyjl
pyjlraw
pyisjl
pyjlvalue
pybinaryio
pytextio
pyjlarray
pyjlset
pyjldict
```

## Arithmetic
Expand Down
11 changes: 11 additions & 0 deletions docs/src/releasenotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

## Unreleased (v1)
* `PythonCall.GC` is now more like `Base.GC`: `enable(true)` replaces `enable()`, `enable(false)` replaces `disable()`, and `gc()` is added.
* Breaking changes to Julia wrapper types:
* Classes renamed: `ValueBase` to `JlBase`, `AnyValue` to `Jl`, `ArrayValue` to `JlArray`, etc.
* Classes removed: `RawValue`, `ModuleValue`, `TypeValue`, `NumberValue`, `ComplexValue`, `RealValue`, `RationalValue`, `IntegerValue`.
* `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.
* `juliacall.Pkg` removed (you can import it yourself).
* `juliacall.convert` removed (use `juliacall.Jl` instead).
* Methods renamed: `_jl_display()` to `jl_display()`, `_jl_help()` to `jl_help()`, etc.
* Methods removed: `_jl_raw()`.
* `pyjl(x)` now always returns a `juliacall.Jl` (it used to select a wrapper type if possible).
* `pyjltype(x)` removed.
* New functions: `pyjlarray`, `pyjldict`, `pyjlset`.

## Unreleased
* `Serialization.serialize` can use `dill` instead of `pickle` by setting the env var `JULIA_PYTHONCALL_PICKLE=dill`.
Expand Down
4 changes: 2 additions & 2 deletions examples/flux.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@
"metadata": {},
"outputs": [],
"source": [
"jl.seval(\"using Flux\")\n",
"jl.jl_eval(\"using Flux\")\n",
"model = jl.Chain(\n",
" jl.Dense(1, 10, jl.relu),\n",
" jl.Dense(10, 10, jl.relu),\n",
" jl.Dense(10, 10, jl.relu),\n",
" jl.Dense(10, 1),\n",
")\n",
"loss = jl.seval(\"m -> (x, y) -> Flux.Losses.mse(m(x), y)\")(model)"
"loss = jl.jl_eval(\"m -> (x, y) -> Flux.Losses.mse(m(x), y)\")(model)"
]
},
{
Expand Down
Loading
Loading