-
-
Notifications
You must be signed in to change notification settings - Fork 44
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
Use julia_project to manage Julia dependency #100
Changes from 6 commits
0ffc3e5
a332293
dc6cd52
97b2b4b
67092f2
9e08135
7bd4d2e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,3 +56,6 @@ docs/_build/ | |
|
||
# PyBuilder | ||
target/ | ||
|
||
**/Manifest.toml | ||
**/*.so |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,7 @@ | ||
include README.md | ||
include LICENSE.md | ||
include docs/*.txt | ||
include diffeqpy/sys_image/*.jl | ||
include diffeqpy/sys_image/Project.toml | ||
include diffeqpy/Project.toml | ||
include diffeqpy/*.jl |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
[deps] | ||
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" | ||
DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa" | ||
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" | ||
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,37 +1,3 @@ | ||
import os | ||
import shutil | ||
import subprocess | ||
import sys | ||
from ._julia_project import julia, compile_diffeqpy, update_diffeqpy | ||
|
||
from jill.install import install_julia | ||
|
||
script_dir = os.path.dirname(os.path.realpath(__file__)) | ||
|
||
|
||
def _find_julia(): | ||
# TODO: this should probably fallback to query jill | ||
return shutil.which("julia") | ||
|
||
|
||
def install(*, confirm=False): | ||
""" | ||
Install Julia (if required) and Julia packages required for diffeqpy. | ||
""" | ||
julia = _find_julia() | ||
if not julia: | ||
print("No Julia version found. Installing Julia.") | ||
install_julia(confirm=confirm) | ||
julia = _find_julia() | ||
if not julia: | ||
raise RuntimeError( | ||
"Julia installed with jill but `julia` binary cannot be found in the path" | ||
) | ||
env = os.environ.copy() | ||
env["PYTHON"] = sys.executable | ||
subprocess.check_call([julia, os.path.join(script_dir, "install.jl")], env=env) | ||
|
||
|
||
def _ensure_installed(*kwargs): | ||
if not _find_julia(): | ||
# TODO: this should probably ensure that packages are installed too | ||
install(*kwargs) | ||
from ._version import __version__ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import julia | ||
import logging | ||
|
||
from julia_project import JuliaProject | ||
|
||
import os | ||
diffeqpy_path = os.path.dirname(os.path.abspath(__file__)) | ||
|
||
julia_project = JuliaProject( | ||
name="diffeqpy", | ||
package_path=diffeqpy_path, | ||
preferred_julia_versions = ['1.7', '1.6', 'latest'], | ||
env_prefix = 'DIFFEQPY_', | ||
logging_level = logging.INFO, # or logging.WARN, | ||
console_logging=False | ||
) | ||
Comment on lines
+9
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reason why to do this in Python? Since Chris is the maintainer of this package, it's very likely he and his contributors want to deal with Julia programs than in Python. That's why I wrote things mainly in Julia (e.g., Is this because of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand the gist, but maybe not precisely what you are asking. Yes, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I 100% agree with this. The only reason I could get my team to agree to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also strongly agree with @jlapeyre , I like Julia very much but I have to force myself to use Python every time because the organization I am associated with deals only in Python, I doubt if anyone will be ready to deal with Julia while working on a project which is all in python. It will be a good idea to hide Julia altogether while appreciating all the benefits that Julia provides :) |
||
|
||
julia_project.run() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I strongly suggest avoiding side-effects on import, at least from the top-level module (i.e., on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't see at the moment when this would happen. But, I suspect you are correct. So, you would have to do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, that's the idea. It'd be better if >>> import diffeqpy
>>> diffeqpy.update(julia=True) # hypothetical API to update DifferentialEquations and Julia
>>> from diffeqpy import de works. If we initialize things in I also consider that it's a language-agnostic best practice to avoid "magic" initialization on module/library import.
I don't think this is related to the package initialization, though. Do you generate a sysimage for each libpython? If so, it might be due to that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, the idea of
You could require that the user restart after upgrading. I think people are pretty used to software that tells them they have to do that.
If there is a conflicting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm OK with
I don't think we need to copy badly designed software when there is a simple and better solution that is already implemented. |
||
|
||
# logger = julia_project.logger | ||
|
||
def compile_diffeqpy(): | ||
""" | ||
Compile a system image for `diffeqpy` in the subdirectory `./sys_image/`. This | ||
system image will be loaded the next time you import `diffeqpy`. | ||
""" | ||
julia_project.compile_julia_project() | ||
|
||
|
||
def update_diffeqpy(): | ||
""" | ||
Remove possible stale Manifest.toml files and compiled system image. | ||
Update Julia packages and rebuild Manifest.toml file. | ||
Before compiling, it's probably a good idea to call this method, then restart Python. | ||
""" | ||
julia_project.update() | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
"""Define version number here and read it from setup.py automatically""" | ||
__version__ = "1.2.0" |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[deps] | ||
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e" | ||
DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa" | ||
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" | ||
PackageCompiler = "9b87118b-4619-50d2-8e1e-99f35a4d4d9d" | ||
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
using DifferentialEquations | ||
const DE = DifferentialEquations | ||
|
||
# This is curiously slow | ||
function sde_exercise() | ||
f = (u,p,t) -> 1.01*u | ||
g = (u,p,t) -> 0.87*u | ||
u0 = 0.5 | ||
tspan = (0.0,1.0) | ||
prob = DE.SDEProblem(f,g,u0,tspan) | ||
sol = DE.solve(prob,reltol=1e-3,abstol=1e-3) | ||
return nothing | ||
end | ||
|
||
function ode_exercise() | ||
f = (u,p,t) -> -u | ||
u0 = 0.5 | ||
tspan = (0., 1.) | ||
prob = DE.ODEProblem(f, u0, tspan) | ||
sol = DE.solve(prob) | ||
return nothing | ||
end | ||
|
||
function ode_exercise2() | ||
f = function(u,p,t) | ||
x, y, z = u | ||
sigma, rho, beta = p | ||
return [sigma * (y - x), x * (rho - z) - y, x * y - beta * z] | ||
end | ||
u0 = [1.0,0.0,0.0] | ||
tspan = (0., 100.) | ||
p = [10.0,28.0,8/3] | ||
prob = DE.ODEProblem(f, u0, tspan, p) | ||
sol = DE.solve(prob,saveat=0.01) | ||
return nothing | ||
end | ||
|
||
# From ODE docs | ||
function ode_exercise3() | ||
f(u,p,t) = 1.01*u | ||
u0 = 1/2 | ||
tspan = (0.0,1.0) | ||
prob = ODEProblem(f,u0,tspan) | ||
sol = solve(prob, Tsit5(), reltol=1e-8, abstol=1e-8) | ||
return nothing | ||
end | ||
|
||
ode_exercise() | ||
ode_exercise2() | ||
ode_exercise3() | ||
sde_exercise() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using PackageCompiler | ||
using Libdl: Libdl | ||
|
||
packages = [:PyCall, :DiffEqBase, :DifferentialEquations, :OrdinaryDiffEq] | ||
|
||
sysimage_path = joinpath(@__DIR__, "sys_julia_project." * Libdl.dlext) | ||
|
||
#create_sysimage(packages; sysimage_path=sysimage_path) | ||
|
||
create_sysimage(packages; sysimage_path=sysimage_path, | ||
precompile_execution_file=joinpath(@__DIR__, "compile_exercise_script.jl")) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,8 +4,14 @@ def readme(): | |
with open('README.md') as f: | ||
return f.read() | ||
|
||
|
||
version = {} | ||
with open("./diffeqpy/_version.py") as fp: | ||
exec(fp.read(), version) | ||
|
||
|
||
setup(name='diffeqpy', | ||
version='1.2.0', | ||
version=version['__version__'], | ||
description='Solving Differential Equations in Python', | ||
long_description=readme(), | ||
long_description_content_type="text/markdown", | ||
|
@@ -24,6 +30,7 @@ def readme(): | |
author_email='[email protected]', | ||
license='MIT', | ||
packages=['diffeqpy','diffeqpy.tests'], | ||
install_requires=['julia>=0.2', 'jill'], | ||
install_requires=['julia>=0.2', | ||
'julia_project>=0.0.23'], | ||
include_package_data=True, | ||
zip_safe=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does
julia_project
generate files insidediffeqpy
directory? I don't think that's a good idea. For example, the directory may not be writable after the installation.I see Pluto is using
~/.julia/environments/__pluto_$VERSION
for its internal env. So, similarly, maybe you can generate~/.julia/environments/__python_julia_project_$VERSION_$SLUG/
where$VERSION
is the Julia version and$SLUG
is a hash of the path of the current python environment (say).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it does. But, more than just Manifest.toml and Project.toml. It optionally puts a depo in
./depot
in thediffeqpy
directory. I also don't like it, but this was easier to start with. I agree it should be changed. Probably under~/.julia/julia_project
. Bothjuliaup
andpythoncall
claim directories there.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's very rare that you'd need a separate depot. If a Julia environment is enough, I think that's much better. It definitely helps reduce precompile time. It's different from how Python virtualenv/venv works but I think a slug-based mapping is enough for bridging the gap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather just use a Julia environment as well. In fact, I do use one. But the separate depot is just to work around the problem of incompatible libpython. I want to avoid having multiple python projects fighting over PyCall, always rebuilding it.... Or maybe I don't follow what you mean here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have a great understanding of the problem with incompatible libpython. My assumption, which I am not sure of, is that it is not enough to have a separate
.ji
file forPyCall
for each python project. Some other.ji
files might need to be recompiled and be incompatible as well. Or, is it enough to use multiple depots and one of them only containsPyCall
(including the compiled.ji
file) ?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure the direction of JuliaPy/PyCall.jl#945 makes this hack unnecessary. It let us configure libpython for each Julia project and so PyCall.jl can be precompiled for each libpython. It'll be a game changer for how Julia packages are used from Python.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow. I looked around quite a bit, but missed this. It's a big deal. I'll have to do some reading to absorb it all.