forked from cctbx/dxtbx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
libtbx_refresh.py
112 lines (91 loc) · 3.66 KB
/
libtbx_refresh.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
from __future__ import annotations
import contextlib
import importlib
import inspect
import io
import os
import subprocess
import sys
import libtbx
import libtbx.pkg_utils
try:
import dials.precommitbx.nagger
dials.precommitbx.nagger.nag()
except ModuleNotFoundError:
pass
def _install_setup_readonly_fallback(package_name: str):
"""
Partially install package in the libtbx build folder.
This is a less complete installation - base python console_scripts
entrypoints will not be installed, but the basic package metadata
and other entrypoints will be enumerable through dispatcher black magic
"""
root_path = libtbx.env.dist_path(package_name)
import_path = os.path.join(root_path, "src")
# Install this into a build/dxtbx subfolder
build_path = abs(libtbx.env.build_path / package_name)
subprocess.run(
[
sys.executable,
"-m",
"pip",
"install",
"--prefix",
build_path,
"--no-build-isolation",
"--no-deps",
"-e",
root_path,
],
check=True,
)
# Get the actual environment being configured (NOT libtbx.env)
env = _get_real_env_hack_hack_hack()
# Update the libtbx environment pythonpaths to point to the source
# location which now has an .egg-info folder; this will mean that
# the PYTHONPATH is written into the libtbx dispatchers
rel_path = libtbx.env.as_relocatable_path(import_path)
if rel_path not in env.pythonpath:
env.pythonpath.insert(0, rel_path)
# Update the sys.path so that we can find the .egg-info in this process
# if we do a full reconstruction of the working set
if import_path not in sys.path:
sys.path.insert(0, import_path)
# ...and make sure it is picked up by the import system
importlib.invalidate_caches()
# This is already generated by this point, but will get picked up
# on the second libtbx.refresh.
module = env.module_dict[package_name]
if f"src/{package_name}" not in module.extra_command_line_locations:
module.extra_command_line_locations.append(f"src/{package_name}")
# Because dispatchers for all modules are generated _before_ any of
# libtbx_refresh are run, then we need to regenerate all of the
# dispatchers now we've added the extra PYTHONPATH
with contextlib.redirect_stdout(io.StringIO()):
for module in env.module_list:
module.process_command_line_directories()
def _get_real_env_hack_hack_hack():
"""
Get the real, currently-being-configured libtbx.env environment.
This is not libtbx.env, because although libtbx.env_config.environment.cold_start
does:
self.pickle()
libtbx.env = self
the first time there is an "import libtbx.load_env" this environment
gets replaced by unpickling the freshly-written libtbx_env file onto
libtbx.env, thereby making the environment accessed via libtbx.env
*not* the actual one that is currently being constructed.
So, the only way to get this environment being configured in order
to - like - configure it, is to walk the stack trace and extract the
self object from environment.refresh directly.
"""
for frame in inspect.stack():
if (
frame.filename.endswith("env_config.py")
and frame.function == "refresh"
and "self" in frame.frame.f_locals
):
return frame.frame.f_locals["self"]
raise RuntimeError("Could not determine real libtbx.env_config.environment object")
# When building in libtbx, always assume it's unsafe to write to base/
_install_setup_readonly_fallback("dxtbx")