MilesCranmer commited on
Commit
3532c08
2 Parent(s): b6ac303 7fec442

Merge pull request #197 from MilesCranmer/custom-julia-project

Browse files
.github/workflows/CI.yml CHANGED
@@ -69,6 +69,8 @@ jobs:
69
  run: pip install torch # (optional import)
70
  - name: "Run Torch tests"
71
  run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test_torch
 
 
72
  - name: Coveralls
73
  env:
74
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -121,6 +123,8 @@ jobs:
121
  run: python3 -m pip install coverage coveralls
122
  - name: "Run tests"
123
  run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test
 
 
124
  - name: Coveralls
125
  env:
126
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
69
  run: pip install torch # (optional import)
70
  - name: "Run Torch tests"
71
  run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test_torch
72
+ - name: "Run custom env tests"
73
+ run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test_env
74
  - name: Coveralls
75
  env:
76
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
123
  run: python3 -m pip install coverage coveralls
124
  - name: "Run tests"
125
  run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test
126
+ - name: "Run custom env tests"
127
+ run: coverage run --append --source=pysr --omit='*/feynman_problems.py' -m unittest test.test_env
128
  - name: Coveralls
129
  env:
130
  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
.github/workflows/CI_Windows.yml CHANGED
@@ -63,3 +63,5 @@ jobs:
63
  run: pip install torch # (optional import)
64
  - name: "Run Torch tests"
65
  run: python -m unittest test.test_torch
 
 
 
63
  run: pip install torch # (optional import)
64
  - name: "Run Torch tests"
65
  run: python -m unittest test.test_torch
66
+ - name: "Run custom env tests"
67
+ run: python -m unittest test.test_env
.github/workflows/CI_conda_forge.yml CHANGED
@@ -34,4 +34,4 @@ jobs:
34
  - name: "Install pysr-forge"
35
  run: conda activate test && mamba install pysr
36
  - name: "Run tests"
37
- run: conda activate test && python3 -m unittest test.test
 
34
  - name: "Install pysr-forge"
35
  run: conda activate test && mamba install pysr
36
  - name: "Run tests"
37
+ run: conda activate test && python3 -m unittest test.test && python3 -m unittest test.test_env
.github/workflows/CI_docker.yml CHANGED
@@ -38,4 +38,4 @@ jobs:
38
  - name: Build docker
39
  run: docker build -t pysr --build-arg ARCH=${{ matrix.arch }} --build-arg VERSION=${{ matrix.julia-version }} --build-arg PYVERSION=${{ matrix.python-version }} .
40
  - name: Test docker
41
- run: docker run --platform=${{ matrix.arch }} --rm pysr /bin/bash -c 'python3 -m unittest test.test'
 
38
  - name: Build docker
39
  run: docker build -t pysr --build-arg ARCH=${{ matrix.arch }} --build-arg VERSION=${{ matrix.julia-version }} --build-arg PYVERSION=${{ matrix.python-version }} .
40
  - name: Test docker
41
+ run: docker run --platform=${{ matrix.arch }} --rm pysr /bin/bash -c 'python3 -m unittest test.test && python3 -m unittest test.test_env'
.github/workflows/CI_docker_large_nightly.yml CHANGED
@@ -31,4 +31,4 @@ jobs:
31
  - name: Build docker
32
  run: docker build -t pysr --build-arg ARCH=${{ matrix.arch }} --build-arg VERSION=${{ matrix.julia-version }} --build-arg PYVERSION=${{ matrix.python-version }} .
33
  - name: Test docker
34
- run: docker run --platform=${{ matrix.arch }} --rm pysr /bin/bash -c 'python3 -m unittest test.test'
 
31
  - name: Build docker
32
  run: docker build -t pysr --build-arg ARCH=${{ matrix.arch }} --build-arg VERSION=${{ matrix.julia-version }} --build-arg PYVERSION=${{ matrix.python-version }} .
33
  - name: Test docker
34
+ run: docker run --platform=${{ matrix.arch }} --rm pysr /bin/bash -c 'python3 -m unittest test.test && python3 -m unittest test.test_env'
.github/workflows/CI_large_nightly.yml CHANGED
@@ -41,4 +41,4 @@ jobs:
41
  python setup.py install
42
  python -c 'import pysr; pysr.install()'
43
  - name: "Run tests"
44
- run: python -m unittest test.test
 
41
  python setup.py install
42
  python -c 'import pysr; pysr.install()'
43
  - name: "Run tests"
44
+ run: python -m unittest test.test && python -m unittest test.test_env
.github/workflows/CI_mac.yml CHANGED
@@ -67,3 +67,5 @@ jobs:
67
  run: pip install torch # (optional import)
68
  - name: "Run Torch tests"
69
  run: python -m unittest test.test_torch
 
 
 
67
  run: pip install torch # (optional import)
68
  - name: "Run Torch tests"
69
  run: python -m unittest test.test_torch
70
+ - name: "Run custom env tests"
71
+ run: python -m unittest test.test_env
environment.yml CHANGED
@@ -9,4 +9,5 @@ dependencies:
9
  - scikit-learn
10
  - setuptools
11
  - pyjulia
12
- - openlibm<0.8.0
 
 
9
  - scikit-learn
10
  - setuptools
11
  - pyjulia
12
+ - openlibm
13
+ - openspecfun
pysr/julia_helpers.py CHANGED
@@ -33,7 +33,9 @@ def _get_julia_env_dir():
33
  # Have to manually get env dir:
34
  try:
35
  julia_env_dir_str = subprocess.run(
36
- ["julia", "-e using Pkg; print(Pkg.envdir())"], capture_output=True
 
 
37
  ).stdout.decode()
38
  except FileNotFoundError:
39
  env_path = os.environ["PATH"]
@@ -54,6 +56,12 @@ def _set_julia_project_env(julia_project, is_shared):
54
  os.environ["JULIA_PROJECT"] = str(julia_project)
55
 
56
 
 
 
 
 
 
 
57
  def install(julia_project=None, quiet=False): # pragma: no cover
58
  """
59
  Install PyCall.jl and all required dependencies for SymbolicRegression.jl.
@@ -64,28 +72,13 @@ def install(julia_project=None, quiet=False): # pragma: no cover
64
 
65
  _version_assertion()
66
  # Set JULIA_PROJECT so that we install in the pysr environment
67
- julia_project, is_shared = _process_julia_project(julia_project)
68
- _set_julia_project_env(julia_project, is_shared)
69
 
70
  julia.install(quiet=quiet)
 
 
71
 
72
- if is_shared:
73
- # is_shared is only true if the julia_project arg was None
74
- # See _process_julia_project
75
- Main = init_julia(None)
76
- else:
77
- Main = init_julia(julia_project)
78
-
79
- Main.eval("using Pkg")
80
-
81
- io = "devnull" if quiet else "stderr"
82
- io_arg = f"io={io}" if is_julia_version_greater_eq(version=(1, 6, 0)) else ""
83
-
84
- # Can't pass IO to Julia call as it evaluates to PyObject, so just directly
85
- # use Main.eval:
86
- Main.eval(
87
- f'Pkg.activate("{_escape_filename(julia_project)}", shared = Bool({int(is_shared)}), {io_arg})'
88
- )
89
  if is_shared:
90
  # Install SymbolicRegression.jl:
91
  _add_sr_to_julia_project(Main, io_arg)
@@ -117,11 +110,14 @@ def _import_error_string(julia_project=None):
117
  def _process_julia_project(julia_project):
118
  if julia_project is None:
119
  is_shared = True
120
- julia_project = f"pysr-{__version__}"
 
 
 
121
  else:
122
  is_shared = False
123
- julia_project = Path(julia_project)
124
- return julia_project, is_shared
125
 
126
 
127
  def is_julia_version_greater_eq(juliainfo=None, version=(1, 6, 0)):
@@ -151,7 +147,7 @@ def _check_for_conflicting_libraries(): # pragma: no cover
151
  )
152
 
153
 
154
- def init_julia(julia_project=None):
155
  """Initialize julia binary, turning off compiled modules if needed."""
156
  global julia_initialized
157
 
@@ -161,8 +157,8 @@ def init_julia(julia_project=None):
161
  from julia.core import JuliaInfo, UnsupportedPythonError
162
 
163
  _version_assertion()
164
- julia_project, is_shared = _process_julia_project(julia_project)
165
- _set_julia_project_env(julia_project, is_shared)
166
 
167
  try:
168
  info = JuliaInfo.load(julia="julia")
@@ -189,11 +185,24 @@ def init_julia(julia_project=None):
189
 
190
  Main = _Main
191
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  julia_initialized = True
193
  return Main
194
 
195
 
196
  def _add_sr_to_julia_project(Main, io_arg):
 
197
  Main.sr_spec = Main.PackageSpec(
198
  name="SymbolicRegression",
199
  url="https://github.com/MilesCranmer/SymbolicRegression.jl",
 
33
  # Have to manually get env dir:
34
  try:
35
  julia_env_dir_str = subprocess.run(
36
+ ["julia", "-e using Pkg; print(Pkg.envdir())"],
37
+ capture_output=True,
38
+ env=os.environ,
39
  ).stdout.decode()
40
  except FileNotFoundError:
41
  env_path = os.environ["PATH"]
 
56
  os.environ["JULIA_PROJECT"] = str(julia_project)
57
 
58
 
59
+ def _get_io_arg(quiet):
60
+ io = "devnull" if quiet else "stderr"
61
+ io_arg = f"io={io}" if is_julia_version_greater_eq(version=(1, 6, 0)) else ""
62
+ return io_arg
63
+
64
+
65
  def install(julia_project=None, quiet=False): # pragma: no cover
66
  """
67
  Install PyCall.jl and all required dependencies for SymbolicRegression.jl.
 
72
 
73
  _version_assertion()
74
  # Set JULIA_PROJECT so that we install in the pysr environment
75
+ processed_julia_project, is_shared = _process_julia_project(julia_project)
76
+ _set_julia_project_env(processed_julia_project, is_shared)
77
 
78
  julia.install(quiet=quiet)
79
+ Main = init_julia(julia_project, quiet=quiet)
80
+ io_arg = _get_io_arg(quiet)
81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  if is_shared:
83
  # Install SymbolicRegression.jl:
84
  _add_sr_to_julia_project(Main, io_arg)
 
110
  def _process_julia_project(julia_project):
111
  if julia_project is None:
112
  is_shared = True
113
+ processed_julia_project = f"pysr-{__version__}"
114
+ elif julia_project[0] == "@":
115
+ is_shared = True
116
+ processed_julia_project = julia_project[1:]
117
  else:
118
  is_shared = False
119
+ processed_julia_project = Path(julia_project)
120
+ return processed_julia_project, is_shared
121
 
122
 
123
  def is_julia_version_greater_eq(juliainfo=None, version=(1, 6, 0)):
 
147
  )
148
 
149
 
150
+ def init_julia(julia_project=None, quiet=False):
151
  """Initialize julia binary, turning off compiled modules if needed."""
152
  global julia_initialized
153
 
 
157
  from julia.core import JuliaInfo, UnsupportedPythonError
158
 
159
  _version_assertion()
160
+ processed_julia_project, is_shared = _process_julia_project(julia_project)
161
+ _set_julia_project_env(processed_julia_project, is_shared)
162
 
163
  try:
164
  info = JuliaInfo.load(julia="julia")
 
185
 
186
  Main = _Main
187
 
188
+ if julia_initialized:
189
+ Main.eval("using Pkg")
190
+
191
+ io_arg = _get_io_arg(quiet)
192
+ # Can't pass IO to Julia call as it evaluates to PyObject, so just directly
193
+ # use Main.eval:
194
+ Main.eval(
195
+ f'Pkg.activate("{_escape_filename(processed_julia_project)}",'
196
+ f"shared = Bool({int(is_shared)}), "
197
+ f"{io_arg})"
198
+ )
199
+
200
  julia_initialized = True
201
  return Main
202
 
203
 
204
  def _add_sr_to_julia_project(Main, io_arg):
205
+ Main.eval("using Pkg")
206
  Main.sr_spec = Main.PackageSpec(
207
  name="SymbolicRegression",
208
  url="https://github.com/MilesCranmer/SymbolicRegression.jl",
test/test_env.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Contains tests for creating and initializing custom Julia projects."""
2
+
3
+ import unittest
4
+ import os
5
+ from pysr import julia_helpers
6
+ from tempfile import TemporaryDirectory
7
+
8
+
9
+ class TestJuliaProject(unittest.TestCase):
10
+ """Various tests for working with Julia projects."""
11
+
12
+ def test_custom_shared_env(self):
13
+ """Test that we can use PySR in a custom shared env."""
14
+ with TemporaryDirectory() as tmpdir:
15
+ # Create a temp depot to store julia packages (and our custom env)
16
+ Main = julia_helpers.init_julia()
17
+
18
+ # Set up env:
19
+ if "JULIA_DEPOT_PATH" not in os.environ:
20
+ old_env = None
21
+ os.environ["JULIA_DEPOT_PATH"] = tmpdir
22
+ else:
23
+ old_env = os.environ["JULIA_DEPOT_PATH"]
24
+ os.environ[
25
+ "JULIA_DEPOT_PATH"
26
+ ] = f"{tmpdir}:{os.environ['JULIA_DEPOT_PATH']}"
27
+ Main.eval(
28
+ f'pushfirst!(DEPOT_PATH, "{julia_helpers._escape_filename(tmpdir)}")'
29
+ )
30
+ test_env_name = "@pysr_test_env"
31
+ julia_helpers.install(julia_project=test_env_name)
32
+ Main = julia_helpers.init_julia(julia_project=test_env_name)
33
+
34
+ # Try to use env:
35
+ Main.eval("using SymbolicRegression")
36
+ Main.eval("using Pkg")
37
+
38
+ # Assert we actually loaded it:
39
+ cur_project_dir = Main.eval("splitdir(dirname(Base.active_project()))[1]")
40
+ potential_shared_project_dirs = Main.eval("Pkg.envdir(DEPOT_PATH[1])")
41
+ self.assertEqual(cur_project_dir, potential_shared_project_dirs)
42
+
43
+ # Clean up:
44
+ Main.eval("pop!(DEPOT_PATH)")
45
+ if old_env is None:
46
+ del os.environ["JULIA_DEPOT_PATH"]
47
+ else:
48
+ os.environ["JULIA_DEPOT_PATH"] = old_env