xobjects 0.6.8__tar.gz → 0.6.9__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. {xobjects-0.6.8/xobjects.egg-info → xobjects-0.6.9}/PKG-INFO +1 -1
  2. xobjects-0.6.9/tests/test_general.py +266 -0
  3. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_struct.py +7 -1
  4. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/__init__.py +1 -0
  5. xobjects-0.6.9/xobjects/_version.py +1 -0
  6. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/context.py +3 -2
  7. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/context_cpu.py +34 -36
  8. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/context_cupy.py +16 -22
  9. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/context_pyopencl.py +3 -2
  10. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/general.py +11 -3
  11. xobjects-0.6.9/xobjects/settings.py +308 -0
  12. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/struct.py +7 -10
  13. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/test_helpers.py +8 -14
  14. {xobjects-0.6.8 → xobjects-0.6.9/xobjects.egg-info}/PKG-INFO +1 -1
  15. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects.egg-info/SOURCES.txt +2 -0
  16. xobjects-0.6.8/xobjects/_version.py +0 -1
  17. {xobjects-0.6.8 → xobjects-0.6.9}/Architecture.md +0 -0
  18. {xobjects-0.6.8 → xobjects-0.6.9}/LICENSE +0 -0
  19. {xobjects-0.6.8 → xobjects-0.6.9}/MANIFEST.in +0 -0
  20. {xobjects-0.6.8 → xobjects-0.6.9}/README.md +0 -0
  21. {xobjects-0.6.8 → xobjects-0.6.9}/pyproject.toml +0 -0
  22. {xobjects-0.6.8 → xobjects-0.6.9}/setup.cfg +0 -0
  23. {xobjects-0.6.8 → xobjects-0.6.9}/setup.py +0 -0
  24. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_align.py +0 -0
  25. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_array.py +0 -0
  26. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_buffer.py +0 -0
  27. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_capi.py +0 -0
  28. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_chunk.py +0 -0
  29. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_common.py +0 -0
  30. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_context_opencl.py +0 -0
  31. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_hybrid_class.py +0 -0
  32. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_kernel.py +0 -0
  33. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_linked_array.py +0 -0
  34. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_nplike_arrays.py +0 -0
  35. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_ref.py +0 -0
  36. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_scalars.py +0 -0
  37. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_shared_memory.py +0 -0
  38. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_strides.py +0 -0
  39. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_string.py +0 -0
  40. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_to_dict.py +0 -0
  41. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_typeutils.py +0 -0
  42. {xobjects-0.6.8 → xobjects-0.6.9}/tests/test_unionref.py +0 -0
  43. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/_patch_pyopencl_array.py +0 -0
  44. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/array.py +0 -0
  45. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/capi.py +0 -0
  46. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/headers/atomicadd.h +0 -0
  47. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/headers/common.h +0 -0
  48. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/hybrid_class.py +0 -0
  49. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/linkedarray.py +0 -0
  50. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/ref.py +0 -0
  51. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/scalar.py +0 -0
  52. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/specialize_source.py +0 -0
  53. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/string.py +0 -0
  54. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/typeutils.py +0 -0
  55. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects/union.py +0 -0
  56. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects.egg-info/dependency_links.txt +0 -0
  57. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects.egg-info/entry_points.txt +0 -0
  58. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects.egg-info/requires.txt +0 -0
  59. {xobjects-0.6.8 → xobjects-0.6.9}/xobjects.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xobjects
3
- Version: 0.6.8
3
+ Version: 0.6.9
4
4
  Summary: In-memory serialization and code generator for CPU and GPU
5
5
  Home-page: https://xsuite.readthedocs.io/
6
6
  Download-URL: https://pypi.python.org/pypi/xobjects
@@ -0,0 +1,266 @@
1
+ import os
2
+ import subprocess
3
+ import sys
4
+
5
+ import pytest
6
+
7
+ import xobjects as xo
8
+ from xobjects.general import Print
9
+ from xobjects.test_helpers import allow_kernel_compilation
10
+
11
+
12
+ def test_print_mode_default(capsys):
13
+ printer = Print()
14
+
15
+ printer('visible')
16
+
17
+ assert capsys.readouterr().out == 'visible\n'
18
+
19
+
20
+ def test_print_mode_suppressed(capsys):
21
+ printer = Print()
22
+ with xo.settings.override(print_mode='suppress'):
23
+ printer('hidden')
24
+
25
+ assert capsys.readouterr().out == ''
26
+
27
+
28
+ def test_settings_control_xsuite_printer(capsys):
29
+ with xo.settings.override(print_mode='suppress'):
30
+ xo._print('hidden')
31
+
32
+ assert capsys.readouterr().out == ''
33
+
34
+
35
+ def test_python_setting_overrides_environment_default(capsys):
36
+ with xo.settings.override(print_mode='print'):
37
+ xo._print('visible')
38
+
39
+ assert capsys.readouterr().out == 'visible\n'
40
+
41
+
42
+ def test_invalid_print_mode_setting():
43
+ with pytest.raises(ValueError, match='XSUITE_PRINT_MODE.*print.*suppress'):
44
+ xo.settings.print_mode = 'invalid'
45
+
46
+
47
+ def test_settings_override_restores_value_after_error():
48
+ original = xo.settings.print_mode
49
+
50
+ with pytest.raises(RuntimeError):
51
+ with xo.settings.override(print_mode='suppress'):
52
+ assert xo.settings.print_mode == 'suppress'
53
+ raise RuntimeError
54
+
55
+ assert xo.settings.print_mode == original
56
+
57
+
58
+ def test_settings_are_discoverable():
59
+ expected_settings = {
60
+ 'print_mode',
61
+ 'progress_indicator',
62
+ 'allow_kernel_compilation',
63
+ 'force_kernel_compilation',
64
+ 'show_kernel_diagnostics',
65
+ 'cffi_forbid_compile',
66
+ 'cffi_keep_build_files',
67
+ 'cuda_backend',
68
+ 'cuda_fast_compile',
69
+ 'cuda_compiler',
70
+ }
71
+
72
+ assert expected_settings <= set(dir(xo.settings))
73
+ for name in expected_settings:
74
+ assert f'{name}=' in repr(xo.settings)
75
+ assert name in type(xo.settings).__doc__
76
+
77
+
78
+ def test_print_mode_environment_variable_is_startup_default():
79
+ environment = os.environ.copy()
80
+ environment['XSUITE_PRINT_MODE'] = 'suppress'
81
+ code = (
82
+ 'import xobjects as xo; '
83
+ 'assert xo.settings.print_mode == "suppress"; '
84
+ 'xo._print("hidden")')
85
+
86
+ completed = subprocess.run(
87
+ [sys.executable, '-c', code],
88
+ env=environment,
89
+ capture_output=True,
90
+ text=True,
91
+ check=True,
92
+ )
93
+
94
+ assert completed.stdout == ''
95
+
96
+
97
+ def test_print_mode_environment_variable_can_be_overridden_in_python():
98
+ environment = os.environ.copy()
99
+ environment['XSUITE_PRINT_MODE'] = 'suppress'
100
+ code = (
101
+ 'import xobjects as xo; '
102
+ 'xo.settings.print_mode = "print"; '
103
+ 'assert xo.settings.print_mode == "print"; '
104
+ 'xo._print("visible")')
105
+
106
+ completed = subprocess.run(
107
+ [sys.executable, '-c', code],
108
+ env=environment,
109
+ capture_output=True,
110
+ text=True,
111
+ check=True,
112
+ )
113
+
114
+ assert completed.stdout == 'visible\n'
115
+
116
+
117
+ def test_environment_change_after_import_does_not_override_python_setting(
118
+ capsys, monkeypatch):
119
+ with xo.settings.override(print_mode='print'):
120
+ monkeypatch.setenv('XSUITE_PRINT_MODE', 'suppress')
121
+ xo._print('visible')
122
+
123
+ assert capsys.readouterr().out == 'visible\n'
124
+
125
+
126
+ @pytest.mark.parametrize('value', ['1', 'true', 'YES', 'on'])
127
+ def test_boolean_environment_true_values(value):
128
+ environment = os.environ.copy()
129
+ environment['XSUITE_ALLOW_KERNEL_COMPILATION'] = value
130
+ code = (
131
+ 'import xobjects as xo; '
132
+ 'assert xo.settings.allow_kernel_compilation is True; '
133
+ 'xo.settings.allow_kernel_compilation = False; '
134
+ 'assert xo.settings.allow_kernel_compilation is False')
135
+
136
+ subprocess.run(
137
+ [sys.executable, '-c', code],
138
+ env=environment,
139
+ capture_output=True,
140
+ text=True,
141
+ check=True,
142
+ )
143
+
144
+
145
+ @pytest.mark.parametrize('value', ['0', 'false', 'NO', 'off'])
146
+ def test_boolean_environment_false_values(value):
147
+ environment = os.environ.copy()
148
+ environment['XSUITE_FORCE_KERNEL_COMPILATION'] = value
149
+ code = (
150
+ 'import xobjects as xo; '
151
+ 'assert xo.settings.force_kernel_compilation is False')
152
+
153
+ subprocess.run(
154
+ [sys.executable, '-c', code],
155
+ env=environment,
156
+ capture_output=True,
157
+ text=True,
158
+ check=True,
159
+ )
160
+
161
+
162
+ def test_invalid_boolean_environment_value():
163
+ environment = os.environ.copy()
164
+ environment['XSUITE_CFFI_FORBID_COMPILE'] = 'sometimes'
165
+
166
+ completed = subprocess.run(
167
+ [sys.executable, '-c', 'import xobjects'],
168
+ env=environment,
169
+ capture_output=True,
170
+ text=True,
171
+ )
172
+
173
+ assert completed.returncode != 0
174
+ assert 'Invalid boolean value' in completed.stderr
175
+ assert 'xobjects.settings.cffi_forbid_compile' in completed.stderr
176
+ assert 'XSUITE_CFFI_FORBID_COMPILE' in completed.stderr
177
+
178
+
179
+ def test_runtime_settings_environment_defaults():
180
+ environment = os.environ.copy()
181
+ environment.update({
182
+ 'XSUITE_PROGRESS_INDICATOR': 'text',
183
+ 'XSUITE_FORCE_KERNEL_COMPILATION': 'yes',
184
+ 'XSUITE_SHOW_KERNEL_DIAGNOSTICS': 'on',
185
+ 'XSUITE_CFFI_FORBID_COMPILE': 'true',
186
+ 'XSUITE_CFFI_KEEP_BUILD_FILES': '1',
187
+ 'XSUITE_CUDA_BACKEND': 'clang',
188
+ 'XSUITE_CUDA_FAST_COMPILE': 'false',
189
+ 'XSUITE_CUDA_COMPILER': '/path/to/clang++',
190
+ })
191
+ code = (
192
+ 'import xobjects as xo; '
193
+ 'assert xo.settings.progress_indicator == "text"; '
194
+ 'assert xo.settings.force_kernel_compilation is True; '
195
+ 'assert xo.settings.show_kernel_diagnostics is True; '
196
+ 'assert xo.settings.cffi_forbid_compile is True; '
197
+ 'assert xo.settings.cffi_keep_build_files is True; '
198
+ 'assert xo.settings.cuda_backend == "clang"; '
199
+ 'assert xo.settings.cuda_fast_compile is False; '
200
+ 'assert xo.settings.cuda_compiler == "/path/to/clang++"')
201
+
202
+ subprocess.run(
203
+ [sys.executable, '-c', code],
204
+ env=environment,
205
+ capture_output=True,
206
+ text=True,
207
+ check=True,
208
+ )
209
+
210
+
211
+ @pytest.mark.parametrize(
212
+ 'allow, force, compilation_allowed',
213
+ [
214
+ (False, False, False),
215
+ (True, False, True),
216
+ (False, True, True),
217
+ (True, True, True),
218
+ ],
219
+ )
220
+ def test_kernel_compilation_settings(allow, force, compilation_allowed):
221
+ with xo.settings.override(
222
+ allow_kernel_compilation=allow,
223
+ force_kernel_compilation=force,
224
+ ):
225
+ assert xo.context_cpu.kernel_compilation_allowed(
226
+ xo.ContextCpu()) is compilation_allowed
227
+
228
+
229
+ def test_user_context_environment_variable(monkeypatch):
230
+ monkeypatch.setenv('XOBJECTS_USER_CONTEXT', 'ContextCpu:auto')
231
+ context = xo.get_user_context()
232
+
233
+ assert context.openmp_enabled
234
+
235
+
236
+ def test_test_contexts_environment_variable(monkeypatch):
237
+ monkeypatch.setenv(
238
+ 'XOBJECTS_TEST_CONTEXTS',
239
+ 'ContextCpu;ContextCpu:auto',
240
+ )
241
+
242
+ contexts = list(xo.context.get_test_contexts())
243
+
244
+ assert len(contexts) == 2
245
+ assert contexts[0].openmp_enabled is False
246
+ assert contexts[1].openmp_enabled is True
247
+
248
+
249
+ def test_cffi_forbid_compile_setting():
250
+ with xo.settings.override(cffi_forbid_compile=True):
251
+ with pytest.raises(RuntimeError) as err:
252
+ xo.ContextCpu().build_kernels({})
253
+
254
+ message = str(err.value)
255
+ assert 'xobjects.settings.cffi_forbid_compile' in message
256
+ assert 'XSUITE_CFFI_FORBID_COMPILE' in message
257
+
258
+
259
+ def test_allow_kernel_compilation_decorator_restores_state():
260
+ @allow_kernel_compilation(skip_when_forbid_compile=False)
261
+ def decorated():
262
+ assert xo.settings.allow_kernel_compilation is True
263
+
264
+ with xo.settings.override(allow_kernel_compilation=False):
265
+ decorated()
266
+ assert xo.settings.allow_kernel_compilation is False
@@ -6,7 +6,11 @@ import cffi
6
6
  import numpy as np
7
7
 
8
8
  import xobjects as xo
9
- from xobjects.test_helpers import for_all_test_contexts, requires_context
9
+ from xobjects.test_helpers import (
10
+ allow_kernel_compilation,
11
+ for_all_test_contexts,
12
+ requires_context,
13
+ )
10
14
 
11
15
 
12
16
  def test_static_struct_def():
@@ -299,6 +303,7 @@ double mult_four(MyStruct stru) {
299
303
 
300
304
 
301
305
  @requires_context("ContextCpu")
306
+ @allow_kernel_compilation
302
307
  def test_compile_kernels_only_if_needed(tmp_path, mocker):
303
308
  """Test the use case of xtrack.
304
309
 
@@ -348,6 +353,7 @@ def test_compile_kernels_only_if_needed(tmp_path, mocker):
348
353
 
349
354
 
350
355
  @requires_context("ContextCpu")
356
+ @allow_kernel_compilation
351
357
  def test_thisclass_placeholder_on_struct():
352
358
  test_context = xo.ContextCpu()
353
359
 
@@ -35,6 +35,7 @@ from .hybrid_class import JEncoder, HybridClass, MetaHybridClass
35
35
  from .linkedarray import BypassLinked
36
36
 
37
37
  from .general import _print
38
+ from .settings import settings
38
39
 
39
40
  from .general import assert_allclose
40
41
 
@@ -0,0 +1 @@
1
+ __version__ = "0.6.9"
@@ -207,6 +207,7 @@ class ModuleNotAvailable(object):
207
207
  class XContext(ABC):
208
208
  minimum_alignment = 1
209
209
  allow_prebuilt_kernels = False
210
+ allow_kernel_compilation = False
210
211
 
211
212
  def __init__(self):
212
213
  self._kernels = KernelDict()
@@ -776,8 +777,8 @@ def get_test_contexts():
776
777
 
777
778
  def get_user_context():
778
779
  """
779
- Get the context specfied by the enviroment variable XOBJECTS_USER_CONTEXT.
780
- If not present use ContextCpu().
780
+ Get the context specified by the environment variable
781
+ ``XOBJECTS_USER_CONTEXT``. If it is not set, use ``ContextCpu()``.
781
782
 
782
783
  Examples:
783
784
  ContextPyopencl:0.0 -> ContextPyopencl(device="0.0")
@@ -13,42 +13,42 @@ from typing import Callable, Dict, List, Sequence, Tuple
13
13
  import weakref
14
14
 
15
15
  from .general import _print
16
+ from .settings import settings
16
17
 
17
18
  import numpy as np
18
19
  import scipy as sp
19
20
 
20
- _forbid_compile = False
21
21
  _suppress_warnings = False
22
- allow_no_prebuilt_kernel = False
23
22
 
24
23
 
25
- def _class_allows_no_prebuilt_kernel(cls):
26
- return (
27
- getattr(cls, "allow_no_prebuilt_kernel", False)
28
- or getattr(
29
- getattr(cls, "_DressingClass", None),
30
- "allow_no_prebuilt_kernel",
31
- False,
32
- )
33
- or getattr(
34
- getattr(cls, "_XoStruct", None), "allow_no_prebuilt_kernel", False
35
- )
36
- )
24
+ def _class_allows_kernel_compilation(cls):
25
+ for candidate in (
26
+ cls,
27
+ getattr(cls, "_DressingClass", None),
28
+ getattr(cls, "_XoStruct", None),
29
+ ):
30
+ if candidate is None:
31
+ continue
32
+ if getattr(candidate, "allow_kernel_compilation", False):
33
+ return True
34
+ # Compatibility with classes from packages that have not migrated yet.
35
+ if getattr(candidate, "allow_no_prebuilt_kernel", False):
36
+ return True
37
+ return False
37
38
 
38
39
 
39
- def allow_no_prebuilt_kernel_enabled(context=None, classes=()):
40
+ def kernel_compilation_allowed(context=None, classes=()):
40
41
  if classes is None:
41
42
  classes = ()
42
43
  elif isinstance(classes, type):
43
44
  classes = (classes,)
44
45
 
45
- if os.environ.get("XSUITE_ALLOW_NO_PREBUILT_KERNELS") is not None:
46
- return True
47
- if allow_no_prebuilt_kernel:
46
+ if (settings.allow_kernel_compilation
47
+ or settings.force_kernel_compilation):
48
48
  return True
49
- if any(_class_allows_no_prebuilt_kernel(cls) for cls in classes):
49
+ if any(_class_allows_kernel_compilation(cls) for cls in classes):
50
50
  return True
51
- return getattr(context, "allow_no_prebuilt_kernel", False)
51
+ return getattr(context, "allow_kernel_compilation", False)
52
52
 
53
53
 
54
54
  def _is_serial_cpu_context(context):
@@ -58,20 +58,20 @@ def _is_serial_cpu_context(context):
58
58
 
59
59
 
60
60
  def require_prebuilt_kernel(context=None, classes=()):
61
- return not allow_no_prebuilt_kernel_enabled(
61
+ return not kernel_compilation_allowed(
62
62
  context, classes=classes
63
63
  ) and _is_serial_cpu_context(context)
64
64
 
65
65
 
66
- def no_prebuilt_kernel_jit_message():
66
+ def kernel_compilation_help_message():
67
67
  return (
68
68
  "To allow just-in-time compilation instead, as in older Xsuite "
69
- "versions, set the environment variable "
70
- "`XSUITE_ALLOW_NO_PREBUILT_KERNELS`, set "
71
- "`xobjects.context_cpu.allow_no_prebuilt_kernel = True`, or set "
72
- "`context.allow_no_prebuilt_kernel = True`. Classes that require "
69
+ "versions, set `xobjects.settings.allow_kernel_compilation = True`, "
70
+ "or equivalently set the environment variable "
71
+ "`XSUITE_ALLOW_KERNEL_COMPILATION=1`, or set "
72
+ "`context.allow_kernel_compilation = True`. Classes that require "
73
73
  "just-in-time compilation can also define "
74
- "`allow_no_prebuilt_kernel = True` as a class attribute. Using "
74
+ "`allow_kernel_compilation = True` as a class attribute. Using "
75
75
  "just-in-time compilation instead of prebuilt kernels may require "
76
76
  "lengthy compilation whenever a different kernel is needed."
77
77
  )
@@ -369,13 +369,11 @@ class ContextCpu(XContext):
369
369
  cdefs = "\n".join(cls._gen_c_decl({}) for cls in classes)
370
370
  cdefs += "\n" + extra_cdef
371
371
 
372
- if _forbid_compile:
373
- raise RuntimeError("Compilation is forbidden")
374
-
375
- if os.environ.get("XOBJECTS_FORBID_COMPILE"):
372
+ if settings.cffi_forbid_compile:
376
373
  raise RuntimeError(
377
- "Compilation is forbidden by the environment variable "
378
- "XOBJECTS_FORBID_COMPILE"
374
+ "CFFI compilation is forbidden by "
375
+ "xobjects.settings.cffi_forbid_compile or equivalently "
376
+ "the environment variable XSUITE_CFFI_FORBID_COMPILE."
379
377
  )
380
378
 
381
379
  so_file = self.compile_kernel(
@@ -536,7 +534,7 @@ class ContextCpu(XContext):
536
534
  return Path(output_file)
537
535
  finally:
538
536
  # Clean temp files
539
- if "XOBJECTS_KEEP_BUILD_FILES" not in os.environ:
537
+ if not settings.cffi_keep_build_files:
540
538
  files_to_remove = [
541
539
  module_name + ".c",
542
540
  module_name + ".o",
@@ -961,8 +959,8 @@ class FFTCpu(object):
961
959
  direction="FFTW_BACKWARD",
962
960
  flags=("FFTW_MEASURE",),
963
961
  )
964
- print(f"fftw simd_aligned={self.fftw.simd_aligned}")
965
- print(f"ifftw simd_aligned={self.fftw.simd_aligned}")
962
+ _print(f"fftw simd_aligned={self.fftw.simd_aligned}")
963
+ _print(f"ifftw simd_aligned={self.fftw.simd_aligned}")
966
964
  else:
967
965
  # I perform one fft to have numpy cache the plan
968
966
  _ = np.fft.ifftn(np.fft.fftn(data, axes=axes), axes=axes)
@@ -25,18 +25,11 @@ from .context import (
25
25
  sources_from_classes,
26
26
  )
27
27
  from .linkedarray import BaseLinkedArray
28
+ from .settings import settings
28
29
  from .specialize_source import specialize_source
29
30
 
30
31
  log = logging.getLogger(__name__)
31
32
 
32
- no_fast_compile = False
33
- """Disable NVRTC fast compile tuning when building CUDA kernels.
34
-
35
- When set to ``True``, ``ContextCupy`` does not pass ``--Ofast-compile=min``
36
- to NVRTC. The ``XO_CUDA_NO_FAST_COMPILE`` environment variable provides the
37
- same behavior.
38
- """
39
-
40
33
  try:
41
34
  import cupy
42
35
  import cupyx.scipy
@@ -399,12 +392,12 @@ class ContextCupy(XContext):
399
392
  Creates a Cupy Context object, that allows performing the computations
400
393
  on nVidia GPUs.
401
394
 
402
- The module-level flag ``xobjects.context_cupy.no_fast_compile`` controls
403
- whether NVRTC fast compile tuning is disabled. By default it is ``False``,
404
- so CUDA kernels built with NVRTC >= 12.9 use ``--Ofast-compile=min`` to
405
- reduce compilation time and memory usage, at the cost of some runtime
406
- performance. Set it to ``True`` to disable this option. The environment
407
- variable ``XO_CUDA_NO_FAST_COMPILE`` also disables it.
395
+ ``xobjects.settings.cuda_fast_compile`` controls whether NVRTC fast compile
396
+ tuning is enabled. By default it is ``True``, so CUDA kernels built with
397
+ NVRTC >= 12.9 use ``--Ofast-compile=min`` to reduce compilation time and
398
+ memory usage, at the cost of some runtime performance. Set
399
+ ``xobjects.settings.cuda_fast_compile = False``, or equivalently the
400
+ environment variable ``XSUITE_CUDA_FAST_COMPILE=0``, to disable it.
408
401
 
409
402
  Args:
410
403
  default_block_size (int): CUDA thread size that is used by default
@@ -439,8 +432,8 @@ class ContextCupy(XContext):
439
432
  self.default_block_size = default_block_size
440
433
  self.default_shared_mem_size_bytes = default_shared_mem_size_bytes
441
434
 
442
- if not backend:
443
- backend = os.environ.get("XO_CUDA_BACKEND", "nvrtc")
435
+ if backend is None:
436
+ backend = settings.cuda_backend
444
437
 
445
438
  if backend not in ["nvrtc", "clang"]:
446
439
  raise ValueError(
@@ -516,9 +509,7 @@ class ContextCupy(XContext):
516
509
  if self.backend == "nvrtc":
517
510
  # NVRTC (default): add NVRTC-specific flags
518
511
  nvrtc_args = (*extra_compile_args,)
519
- fast_compile = not (
520
- no_fast_compile or os.environ.get("XO_CUDA_NO_FAST_COMPILE")
521
- )
512
+ fast_compile = settings.cuda_fast_compile
522
513
  if nvrtc and nvrtc.getVersion() >= (12, 9):
523
514
  # If supported, skip prohibitively heavy optimisations (e.g.
524
515
  # involving cloning). This it at the expense of <20%
@@ -558,7 +549,7 @@ class ContextCupy(XContext):
558
549
  return out_kernels
559
550
 
560
551
  def _find_clang(self):
561
- override = os.environ.get("XO_CUDA_CLANG")
552
+ override = settings.cuda_compiler
562
553
  if override:
563
554
  return override
564
555
 
@@ -567,8 +558,11 @@ class ContextCupy(XContext):
567
558
  return found
568
559
 
569
560
  raise RuntimeError(
570
- "clang++ for the CUDA context not found. Either install clang so that 'clang++' is on PATH,"
571
- "or set the XO_CUDA_CLANG variable to the desired clang++ executable."
561
+ "clang++ for the CUDA context not found. Either install clang so "
562
+ "that 'clang++' is on PATH, or set "
563
+ "xobjects.settings.cuda_compiler, or equivalently the environment "
564
+ "variable XSUITE_CUDA_COMPILER, to the desired clang++ "
565
+ "executable."
572
566
  )
573
567
 
574
568
  def _build_module_with_clang(self, source, extra_compile_args=()):
@@ -21,6 +21,7 @@ from .context import (
21
21
  )
22
22
  from .linkedarray import BaseLinkedArray
23
23
  from .specialize_source import specialize_source
24
+ from .general import _print
24
25
 
25
26
  log = logging.getLogger(__name__)
26
27
 
@@ -100,9 +101,9 @@ class ContextPyopencl(XContext):
100
101
  @classmethod
101
102
  def print_devices(cls):
102
103
  for ip, platform in enumerate(cl.get_platforms()):
103
- print(f"Platform {ip} : {platform.name}")
104
+ _print(f"Platform {ip} : {platform.name}")
104
105
  for id, device in enumerate(platform.get_devices()):
105
- print(f"Device {ip}.{id}: {device.name}")
106
+ _print(f"Device {ip}.{id}: {device.name}")
106
107
 
107
108
  def __init__(
108
109
  self, device=None, patch_pyopencl_array=True, minimum_alignment=None
@@ -5,13 +5,21 @@
5
5
  from numpy.testing import assert_allclose as np_assert_allclose
6
6
  import numpy as np
7
7
 
8
+ from .settings import settings
9
+
8
10
 
9
11
  class Print:
10
- suppress = False
12
+ """Configurable wrapper around :func:`print` used by Xsuite.
13
+
14
+ The behavior is controlled by ``xobjects.settings.print_mode``, or
15
+ equivalently the environment variable ``XSUITE_PRINT_MODE``.
16
+ """
11
17
 
12
18
  def __call__(self, *args, **kwargs):
13
- if not self.suppress:
14
- print(*args, **kwargs)
19
+ if settings.print_mode == 'suppress':
20
+ return
21
+
22
+ print(*args, **kwargs)
15
23
 
16
24
 
17
25
  _print = Print()
@@ -0,0 +1,308 @@
1
+ import os
2
+ from contextlib import contextmanager
3
+
4
+
5
+ class Settings:
6
+ """Process-wide settings shared by the Xsuite packages.
7
+
8
+ Available settings and their equivalent environment variables are:
9
+
10
+ ``print_mode`` — ``XSUITE_PRINT_MODE``
11
+ Values: ``print`` (default), ``suppress``
12
+ Controls informational output produced by Xsuite.
13
+
14
+ ``progress_indicator`` — ``XSUITE_PROGRESS_INDICATOR``
15
+ Values: ``tqdm`` (default), ``text``, ``suppress``
16
+ Selects how progress is displayed when printing is enabled.
17
+
18
+ ``allow_kernel_compilation`` — ``XSUITE_ALLOW_KERNEL_COMPILATION``
19
+ Values: ``False`` (default), ``True``
20
+ Allows compilation when no compatible prebuilt kernel is available.
21
+
22
+ ``force_kernel_compilation`` — ``XSUITE_FORCE_KERNEL_COMPILATION``
23
+ Values: ``False`` (default), ``True``
24
+ Always compiles kernels and skips prebuilt-kernel lookup. Intended
25
+ primarily for Xsuite development and debugging.
26
+
27
+ ``show_kernel_diagnostics`` — ``XSUITE_SHOW_KERNEL_DIAGNOSTICS``
28
+ Values: ``False`` (default), ``True``
29
+ During prebuilt-kernel selection, reports why each candidate kernel is
30
+ selected or rejected.
31
+
32
+ ``cffi_forbid_compile`` — ``XSUITE_CFFI_FORBID_COMPILE``
33
+ Values: ``False`` (default), ``True``
34
+ Prevents CFFI from compiling CPU kernels. Used primarily for debugging
35
+ and testing.
36
+
37
+ ``cffi_keep_build_files`` — ``XSUITE_CFFI_KEEP_BUILD_FILES``
38
+ Values: ``False`` (default), ``True``
39
+ Keeps the intermediate files generated by CFFI. Used primarily for
40
+ debugging and testing.
41
+
42
+ ``cuda_backend`` — ``XSUITE_CUDA_BACKEND``
43
+ Values: ``nvrtc`` (default), ``clang``
44
+ Selects the compiler backend used for CUDA kernels.
45
+
46
+ ``cuda_fast_compile`` — ``XSUITE_CUDA_FAST_COMPILE``
47
+ Values: ``True`` (default), ``False``
48
+ Enables faster NVRTC compilation when supported.
49
+
50
+ ``cuda_compiler`` — ``XSUITE_CUDA_COMPILER``
51
+ Values: ``None`` (default), executable path
52
+ Selects the clang++ executable used by the CUDA clang backend.
53
+
54
+ Notes
55
+ -----
56
+ The same object is exposed as ``xobjects.settings`` and
57
+ ``xtrack.settings``. Environment variables provide its initial values when
58
+ Xobjects is imported. Later Python assignments take precedence and can be
59
+ applied temporarily with :meth:`override`.
60
+
61
+ Boolean environment variables accept ``1``/``0``, ``true``/``false``,
62
+ ``yes``/``no``, and ``on``/``off``, case-insensitively.
63
+
64
+ ``force_kernel_compilation=True`` skips prebuilt-kernel lookup and takes
65
+ precedence over ``allow_kernel_compilation``. When forcing is disabled,
66
+ ``allow_kernel_compilation=True`` permits compilation only if a compatible
67
+ prebuilt kernel is unavailable. Compilation can also be enabled for one
68
+ context or element class by setting its ``allow_kernel_compilation``
69
+ attribute to ``True``. The legacy class attribute
70
+ ``allow_no_prebuilt_kernel`` is temporarily recognized for compatibility
71
+ with packages that have not migrated yet.
72
+
73
+ Examples
74
+ --------
75
+ Allow Xsuite to compile a kernel when no compatible prebuilt kernel is
76
+ available:
77
+
78
+ >>> import xtrack as xt
79
+ >>> xt.settings.allow_kernel_compilation = True
80
+
81
+ Equivalently, set ``XSUITE_ALLOW_KERNEL_COMPILATION=1`` before starting
82
+ Python.
83
+ """
84
+
85
+ def __init__(self):
86
+ object.__setattr__(self, '_definitions', {})
87
+ object.__setattr__(self, '_values', {})
88
+
89
+ def _register(
90
+ self,
91
+ name,
92
+ *,
93
+ default,
94
+ environment_variable=None,
95
+ choices=None,
96
+ environment_parser=None,
97
+ value_type=None,
98
+ ):
99
+ if name in self._definitions:
100
+ raise ValueError(f'Setting {name!r} is already registered.')
101
+
102
+ definition = {
103
+ 'environment_variable': environment_variable,
104
+ 'choices': choices,
105
+ 'value_type': value_type,
106
+ }
107
+ self._definitions[name] = definition
108
+
109
+ value = default
110
+ if (environment_variable is not None
111
+ and environment_variable in os.environ):
112
+ environment_value = os.environ[environment_variable]
113
+ try:
114
+ value = (environment_parser(environment_value)
115
+ if environment_parser else environment_value)
116
+ except (TypeError, ValueError) as err:
117
+ raise ValueError(
118
+ f'Invalid value for '
119
+ f'{self._setting_description(name, definition)}: '
120
+ f'{err}') from err
121
+ self._set(name, value)
122
+
123
+ def _set(self, name, value):
124
+ try:
125
+ definition = self._definitions[name]
126
+ except KeyError as err:
127
+ raise AttributeError(f'Unknown Xsuite setting {name!r}.') from err
128
+
129
+ choices = definition['choices']
130
+ value_type = definition['value_type']
131
+ setting_description = self._setting_description(name, definition)
132
+ if value_type is not None and not isinstance(value, value_type):
133
+ raise TypeError(
134
+ f'Invalid value {value!r} for {setting_description}; '
135
+ f'expected {self._type_name(value_type)}.')
136
+ if choices is not None and value not in choices:
137
+ expected = ', '.join(repr(choice) for choice in choices)
138
+ raise ValueError(
139
+ f'Invalid value {value!r} for {setting_description}; '
140
+ f'expected one of {expected}.')
141
+
142
+ self._values[name] = value
143
+
144
+ def __getattr__(self, name):
145
+ try:
146
+ return self._values[name]
147
+ except KeyError as err:
148
+ raise AttributeError(f'Unknown Xsuite setting {name!r}.') from err
149
+
150
+ def __setattr__(self, name, value):
151
+ self._set(name, value)
152
+
153
+ @contextmanager
154
+ def override(self, **kwargs):
155
+ """Temporarily override settings and restore them on exit."""
156
+ previous = {}
157
+ for name, value in kwargs.items():
158
+ if name not in self._definitions:
159
+ raise AttributeError(f'Unknown Xsuite setting {name!r}.')
160
+ previous[name] = getattr(self, name)
161
+
162
+ # Validate every value before changing any setting.
163
+ for name, value in kwargs.items():
164
+ definition = self._definitions[name]
165
+ setting_description = self._setting_description(name, definition)
166
+ value_type = definition['value_type']
167
+ if value_type is not None and not isinstance(value, value_type):
168
+ raise TypeError(
169
+ f'Invalid value {value!r} for {setting_description}; '
170
+ f'expected {self._type_name(value_type)}.')
171
+ choices = definition['choices']
172
+ if choices is not None and value not in choices:
173
+ expected = ', '.join(repr(choice) for choice in choices)
174
+ raise ValueError(
175
+ f'Invalid value {value!r} for {setting_description}; '
176
+ f'expected one of {expected}.')
177
+
178
+ try:
179
+ for name, value in kwargs.items():
180
+ self._set(name, value)
181
+ yield self
182
+ finally:
183
+ for name, value in previous.items():
184
+ self._set(name, value)
185
+
186
+ def __repr__(self):
187
+ values = ', '.join(
188
+ f'{name}={getattr(self, name)!r}' for name in self._values)
189
+ return f'Settings({values})'
190
+
191
+ def __dir__(self):
192
+ return sorted(set(super().__dir__()) | set(self._definitions))
193
+
194
+ @staticmethod
195
+ def _type_name(value_type):
196
+ if isinstance(value_type, tuple):
197
+ return ' or '.join(tt.__name__ for tt in value_type)
198
+ return value_type.__name__
199
+
200
+ @staticmethod
201
+ def _setting_description(name, definition):
202
+ description = f'Python setting xobjects.settings.{name}'
203
+ environment_variable = definition['environment_variable']
204
+ if environment_variable is not None:
205
+ description += (
206
+ f' or equivalently the environment variable '
207
+ f'{environment_variable}'
208
+ )
209
+ return description
210
+
211
+
212
+ def _parse_boolean(value):
213
+ normalized = value.strip().lower()
214
+ if normalized in ('1', 'true', 'yes', 'on'):
215
+ return True
216
+ if normalized in ('0', 'false', 'no', 'off'):
217
+ return False
218
+ raise ValueError(
219
+ f'Invalid boolean value {value!r}; expected one of 1, 0, true, '
220
+ 'false, yes, no, on, or off.')
221
+
222
+
223
+ def _parse_choice(value):
224
+ return value.strip().lower()
225
+
226
+
227
+ def _parse_optional_string(value):
228
+ value = value.strip()
229
+ return value if value else None
230
+
231
+
232
+ settings = Settings()
233
+ settings._register(
234
+ 'print_mode',
235
+ default='print',
236
+ environment_variable='XSUITE_PRINT_MODE',
237
+ choices=('print', 'suppress'),
238
+ environment_parser=_parse_choice,
239
+ )
240
+ settings._register(
241
+ 'progress_indicator',
242
+ default='tqdm',
243
+ environment_variable='XSUITE_PROGRESS_INDICATOR',
244
+ choices=('tqdm', 'text', 'suppress'),
245
+ environment_parser=_parse_choice,
246
+ )
247
+ settings._register(
248
+ 'allow_kernel_compilation',
249
+ default=False,
250
+ environment_variable='XSUITE_ALLOW_KERNEL_COMPILATION',
251
+ choices=(False, True),
252
+ environment_parser=_parse_boolean,
253
+ value_type=bool,
254
+ )
255
+ settings._register(
256
+ 'force_kernel_compilation',
257
+ default=False,
258
+ environment_variable='XSUITE_FORCE_KERNEL_COMPILATION',
259
+ choices=(False, True),
260
+ environment_parser=_parse_boolean,
261
+ value_type=bool,
262
+ )
263
+ settings._register(
264
+ 'show_kernel_diagnostics',
265
+ default=False,
266
+ environment_variable='XSUITE_SHOW_KERNEL_DIAGNOSTICS',
267
+ choices=(False, True),
268
+ environment_parser=_parse_boolean,
269
+ value_type=bool,
270
+ )
271
+ settings._register(
272
+ 'cffi_forbid_compile',
273
+ default=False,
274
+ environment_variable='XSUITE_CFFI_FORBID_COMPILE',
275
+ choices=(False, True),
276
+ environment_parser=_parse_boolean,
277
+ value_type=bool,
278
+ )
279
+ settings._register(
280
+ 'cffi_keep_build_files',
281
+ default=False,
282
+ environment_variable='XSUITE_CFFI_KEEP_BUILD_FILES',
283
+ choices=(False, True),
284
+ environment_parser=_parse_boolean,
285
+ value_type=bool,
286
+ )
287
+ settings._register(
288
+ 'cuda_backend',
289
+ default='nvrtc',
290
+ environment_variable='XSUITE_CUDA_BACKEND',
291
+ choices=('nvrtc', 'clang'),
292
+ environment_parser=_parse_choice,
293
+ )
294
+ settings._register(
295
+ 'cuda_fast_compile',
296
+ default=True,
297
+ environment_variable='XSUITE_CUDA_FAST_COMPILE',
298
+ choices=(False, True),
299
+ environment_parser=_parse_boolean,
300
+ value_type=bool,
301
+ )
302
+ settings._register(
303
+ 'cuda_compiler',
304
+ default=None,
305
+ environment_variable='XSUITE_CUDA_COMPILER',
306
+ environment_parser=_parse_optional_string,
307
+ value_type=(str, type(None)),
308
+ )
@@ -59,7 +59,7 @@ from .typeutils import (
59
59
  default_conf,
60
60
  )
61
61
 
62
- from .general import Print
62
+ from .settings import settings
63
63
  from .scalar import Int64
64
64
  from .array import Array
65
65
  from .context import Source, Arg, Kernel
@@ -518,14 +518,13 @@ class Struct(metaclass=MetaStruct):
518
518
  extra_classes=(),
519
519
  extra_compile_args=(),
520
520
  ):
521
- if context.allow_prebuilt_kernels:
522
- _print_state = Print.suppress
523
- Print.suppress = True
524
- try:
521
+ if (context.allow_prebuilt_kernels
522
+ and not settings.force_kernel_compilation):
523
+ with settings.override(print_mode="suppress"):
525
524
  try:
526
525
  from xsuite import (
527
526
  get_suitable_kernel,
528
- XSK_PREBUILT_KERNELS_LOCATION,
527
+ PREBUILT_KERNELS_LOCATION,
529
528
  )
530
529
 
531
530
  kernel_info = get_suitable_kernel(
@@ -546,14 +545,12 @@ class Struct(metaclass=MetaStruct):
546
545
  "Xsuite is required to load prebuilt kernels but could "
547
546
  "not be imported. Please install it with "
548
547
  f"`pip install xsuite`. "
549
- f"{context_cpu.no_prebuilt_kernel_jit_message()}"
548
+ f"{context_cpu.kernel_compilation_help_message()}"
550
549
  ) from err
551
- finally:
552
- Print.suppress = _print_state
553
550
  if kernel_info:
554
551
  kernels = context.kernels_from_file(
555
552
  module_name=kernel_info["module_name"],
556
- containing_dir=XSK_PREBUILT_KERNELS_LOCATION,
553
+ containing_dir=PREBUILT_KERNELS_LOCATION,
557
554
  kernel_descriptions=cls._kernels,
558
555
  )
559
556
  context.kernels.update(kernels)
@@ -5,11 +5,11 @@
5
5
 
6
6
  from functools import wraps
7
7
  from typing import Callable, Iterable, Union
8
- import os
9
8
 
10
9
  import pytest
11
10
 
12
11
  from .context import get_context_from_string, get_test_contexts
12
+ from .settings import settings
13
13
 
14
14
 
15
15
  def _for_all_test_contexts_excluding(
@@ -108,13 +108,13 @@ def fix_random_seed(seed: int):
108
108
  return decorator
109
109
 
110
110
 
111
- def allow_no_prebuilt_kernels(
111
+ def allow_kernel_compilation(
112
112
  test_function=None, *, skip_when_forbid_compile=True
113
113
  ):
114
114
  """Allow JIT compilation for tests that intentionally compile kernels.
115
115
 
116
116
  By default, the wrapped test is skipped when compilation is forbidden by
117
- ``XOBJECTS_FORBID_COMPILE``. Use ``skip_when_forbid_compile=False`` when
117
+ ``XSUITE_CFFI_FORBID_COMPILE``. Use ``skip_when_forbid_compile=False`` when
118
118
  the test has more specific ``skip_if_forbid_compile()`` guards inside the
119
119
  test.
120
120
  """
@@ -124,15 +124,8 @@ def allow_no_prebuilt_kernels(
124
124
  def wrapper(*args, **kwargs):
125
125
  if skip_when_forbid_compile:
126
126
  skip_if_forbid_compile()
127
- old_value = os.environ.get("XSUITE_ALLOW_NO_PREBUILT_KERNELS")
128
- os.environ["XSUITE_ALLOW_NO_PREBUILT_KERNELS"] = "1"
129
- try:
127
+ with settings.override(allow_kernel_compilation=True):
130
128
  return test_function(*args, **kwargs)
131
- finally:
132
- if old_value is None:
133
- del os.environ["XSUITE_ALLOW_NO_PREBUILT_KERNELS"]
134
- else:
135
- os.environ["XSUITE_ALLOW_NO_PREBUILT_KERNELS"] = old_value
136
129
 
137
130
  return wrapper
138
131
 
@@ -142,8 +135,9 @@ def allow_no_prebuilt_kernels(
142
135
 
143
136
 
144
137
  def skip_if_forbid_compile():
145
- if os.environ.get("XOBJECTS_FORBID_COMPILE"):
138
+ if settings.cffi_forbid_compile:
146
139
  pytest.skip(
147
- "Compilation is forbidden by the environment variable "
148
- "XOBJECTS_FORBID_COMPILE"
140
+ "CFFI compilation is forbidden by "
141
+ "xobjects.settings.cffi_forbid_compile or equivalently the "
142
+ "environment variable XSUITE_CFFI_FORBID_COMPILE."
149
143
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xobjects
3
- Version: 0.6.8
3
+ Version: 0.6.9
4
4
  Summary: In-memory serialization and code generator for CPU and GPU
5
5
  Home-page: https://xsuite.readthedocs.io/
6
6
  Download-URL: https://pypi.python.org/pypi/xobjects
@@ -12,6 +12,7 @@ tests/test_capi.py
12
12
  tests/test_chunk.py
13
13
  tests/test_common.py
14
14
  tests/test_context_opencl.py
15
+ tests/test_general.py
15
16
  tests/test_hybrid_class.py
16
17
  tests/test_kernel.py
17
18
  tests/test_linked_array.py
@@ -39,6 +40,7 @@ xobjects/hybrid_class.py
39
40
  xobjects/linkedarray.py
40
41
  xobjects/ref.py
41
42
  xobjects/scalar.py
43
+ xobjects/settings.py
42
44
  xobjects/specialize_source.py
43
45
  xobjects/string.py
44
46
  xobjects/struct.py
@@ -1 +0,0 @@
1
- __version__ = "0.6.8"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes